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> 526035Sstevep #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 /* 138*7128Samaguire * Any incompatible change to the below schema should bump the version number. 139*7128Samaguire * The schema has been changed to support value ordering, but this change 140*7128Samaguire * is backwards-compatible - i.e. a previous svc.configd can use a 141*7128Samaguire * repository database with the new schema perfectly well. As a result, 142*7128Samaguire * the schema version has not been updated, allowing downgrade of systems 143*7128Samaguire * without losing repository data. 1440Sstevel@tonic-gate */ 1450Sstevel@tonic-gate #define BACKEND_SCHEMA_VERSION 5 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate static struct backend_tbl_info tbls_normal[] = { /* BACKEND_TYPE_NORMAL */ 1480Sstevel@tonic-gate /* 1490Sstevel@tonic-gate * service_tbl holds all services. svc_id is the identifier of the 1500Sstevel@tonic-gate * service. 1510Sstevel@tonic-gate */ 1520Sstevel@tonic-gate { 1530Sstevel@tonic-gate "service_tbl", 1540Sstevel@tonic-gate "svc_id INTEGER PRIMARY KEY," 1550Sstevel@tonic-gate "svc_name CHAR(256) NOT NULL" 1560Sstevel@tonic-gate }, 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate /* 1590Sstevel@tonic-gate * instance_tbl holds all of the instances. The parent service id 1600Sstevel@tonic-gate * is instance_svc. 1610Sstevel@tonic-gate */ 1620Sstevel@tonic-gate { 1630Sstevel@tonic-gate "instance_tbl", 1640Sstevel@tonic-gate "instance_id INTEGER PRIMARY KEY," 1650Sstevel@tonic-gate "instance_name CHAR(256) NOT NULL," 1660Sstevel@tonic-gate "instance_svc INTEGER NOT NULL" 1670Sstevel@tonic-gate }, 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate /* 1700Sstevel@tonic-gate * snapshot_lnk_tbl links (instance, snapshot name) with snapshots. 1710Sstevel@tonic-gate */ 1720Sstevel@tonic-gate { 1730Sstevel@tonic-gate "snapshot_lnk_tbl", 1740Sstevel@tonic-gate "lnk_id INTEGER PRIMARY KEY," 1750Sstevel@tonic-gate "lnk_inst_id INTEGER NOT NULL," 1760Sstevel@tonic-gate "lnk_snap_name CHAR(256) NOT NULL," 1770Sstevel@tonic-gate "lnk_snap_id INTEGER NOT NULL" 1780Sstevel@tonic-gate }, 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 1810Sstevel@tonic-gate * snaplevel_tbl maps a snapshot id to a set of named, ordered 1820Sstevel@tonic-gate * snaplevels. 1830Sstevel@tonic-gate */ 1840Sstevel@tonic-gate { 1850Sstevel@tonic-gate "snaplevel_tbl", 1860Sstevel@tonic-gate "snap_id INTEGER NOT NULL," 1870Sstevel@tonic-gate "snap_level_num INTEGER NOT NULL," 1880Sstevel@tonic-gate "snap_level_id INTEGER NOT NULL," 1890Sstevel@tonic-gate "snap_level_service_id INTEGER NOT NULL," 1900Sstevel@tonic-gate "snap_level_service CHAR(256) NOT NULL," 1910Sstevel@tonic-gate "snap_level_instance_id INTEGER NULL," 1920Sstevel@tonic-gate "snap_level_instance CHAR(256) NULL" 1930Sstevel@tonic-gate }, 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate /* 1960Sstevel@tonic-gate * snaplevel_lnk_tbl links snaplevels to property groups. 1970Sstevel@tonic-gate * snaplvl_pg_* is identical to the original property group, 1980Sstevel@tonic-gate * and snaplvl_gen_id overrides the generation number. 1990Sstevel@tonic-gate * The service/instance ids are as in the snaplevel. 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate { 2020Sstevel@tonic-gate "snaplevel_lnk_tbl", 2030Sstevel@tonic-gate "snaplvl_level_id INTEGER NOT NULL," 2040Sstevel@tonic-gate "snaplvl_pg_id INTEGER NOT NULL," 2050Sstevel@tonic-gate "snaplvl_pg_name CHAR(256) NOT NULL," 2060Sstevel@tonic-gate "snaplvl_pg_type CHAR(256) NOT NULL," 2070Sstevel@tonic-gate "snaplvl_pg_flags INTEGER NOT NULL," 2080Sstevel@tonic-gate "snaplvl_gen_id INTEGER NOT NULL" 2090Sstevel@tonic-gate }, 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate { NULL, NULL } 2120Sstevel@tonic-gate }; 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate static struct backend_idx_info idxs_normal[] = { /* BACKEND_TYPE_NORMAL */ 2150Sstevel@tonic-gate { "service_tbl", "name", "svc_name" }, 2160Sstevel@tonic-gate { "instance_tbl", "name", "instance_svc, instance_name" }, 2170Sstevel@tonic-gate { "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" }, 2180Sstevel@tonic-gate { "snapshot_lnk_tbl", "snapid", "lnk_snap_id" }, 2190Sstevel@tonic-gate { "snaplevel_tbl", "id", "snap_id" }, 2200Sstevel@tonic-gate { "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" }, 2210Sstevel@tonic-gate { "snaplevel_lnk_tbl", "level", "snaplvl_level_id" }, 2220Sstevel@tonic-gate { NULL, NULL, NULL } 2230Sstevel@tonic-gate }; 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate static struct backend_tbl_info tbls_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2260Sstevel@tonic-gate { NULL, NULL } 2270Sstevel@tonic-gate }; 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate static struct backend_idx_info idxs_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2300Sstevel@tonic-gate { NULL, NULL, NULL } 2310Sstevel@tonic-gate }; 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate static struct backend_tbl_info tbls_common[] = { /* all backend types */ 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * pg_tbl defines property groups. They are associated with a single 2360Sstevel@tonic-gate * service or instance. The pg_gen_id links them with the latest 2370Sstevel@tonic-gate * "edited" version of its properties. 2380Sstevel@tonic-gate */ 2390Sstevel@tonic-gate { 2400Sstevel@tonic-gate "pg_tbl", 2410Sstevel@tonic-gate "pg_id INTEGER PRIMARY KEY," 2420Sstevel@tonic-gate "pg_parent_id INTEGER NOT NULL," 2430Sstevel@tonic-gate "pg_name CHAR(256) NOT NULL," 2440Sstevel@tonic-gate "pg_type CHAR(256) NOT NULL," 2450Sstevel@tonic-gate "pg_flags INTEGER NOT NULL," 2460Sstevel@tonic-gate "pg_gen_id INTEGER NOT NULL" 2470Sstevel@tonic-gate }, 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate /* 2500Sstevel@tonic-gate * prop_lnk_tbl links a particular pg_id and gen_id to a set of 2510Sstevel@tonic-gate * (prop_name, prop_type, val_id) trios. 2520Sstevel@tonic-gate */ 2530Sstevel@tonic-gate { 2540Sstevel@tonic-gate "prop_lnk_tbl", 2550Sstevel@tonic-gate "lnk_prop_id INTEGER PRIMARY KEY," 2560Sstevel@tonic-gate "lnk_pg_id INTEGER NOT NULL," 2570Sstevel@tonic-gate "lnk_gen_id INTEGER NOT NULL," 2580Sstevel@tonic-gate "lnk_prop_name CHAR(256) NOT NULL," 2590Sstevel@tonic-gate "lnk_prop_type CHAR(2) NOT NULL," 2600Sstevel@tonic-gate "lnk_val_id INTEGER" 2610Sstevel@tonic-gate }, 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate /* 2640Sstevel@tonic-gate * value_tbl maps a value_id to a set of values. For any given 265*7128Samaguire * value_id, value_type is constant. The table definition here 266*7128Samaguire * is repeated in backend_check_upgrade(), and must be kept in-sync. 2670Sstevel@tonic-gate */ 2680Sstevel@tonic-gate { 2690Sstevel@tonic-gate "value_tbl", 2700Sstevel@tonic-gate "value_id INTEGER NOT NULL," 2710Sstevel@tonic-gate "value_type CHAR(1) NOT NULL," 272*7128Samaguire "value_value VARCHAR NOT NULL," 273*7128Samaguire "value_order INTEGER DEFAULT 0" 2740Sstevel@tonic-gate }, 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate /* 2770Sstevel@tonic-gate * id_tbl has one row per id space 2780Sstevel@tonic-gate */ 2790Sstevel@tonic-gate { 2800Sstevel@tonic-gate "id_tbl", 2810Sstevel@tonic-gate "id_name STRING NOT NULL," 2820Sstevel@tonic-gate "id_next INTEGER NOT NULL" 2830Sstevel@tonic-gate }, 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * schema_version has a single row, which contains 2870Sstevel@tonic-gate * BACKEND_SCHEMA_VERSION at the time of creation. 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate { 2900Sstevel@tonic-gate "schema_version", 2910Sstevel@tonic-gate "schema_version INTEGER" 2920Sstevel@tonic-gate }, 2930Sstevel@tonic-gate { NULL, NULL } 2940Sstevel@tonic-gate }; 2950Sstevel@tonic-gate 296*7128Samaguire /* 297*7128Samaguire * The indexing of value_tbl is repeated in backend_check_upgrade() and 298*7128Samaguire * must be kept in sync with the indexing specification here. 299*7128Samaguire */ 3000Sstevel@tonic-gate static struct backend_idx_info idxs_common[] = { /* all backend types */ 3010Sstevel@tonic-gate { "pg_tbl", "parent", "pg_parent_id" }, 3020Sstevel@tonic-gate { "pg_tbl", "name", "pg_parent_id, pg_name" }, 3030Sstevel@tonic-gate { "pg_tbl", "type", "pg_parent_id, pg_type" }, 3040Sstevel@tonic-gate { "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" }, 3050Sstevel@tonic-gate { "prop_lnk_tbl", "val", "lnk_val_id" }, 3060Sstevel@tonic-gate { "value_tbl", "id", "value_id" }, 3070Sstevel@tonic-gate { "id_tbl", "id", "id_name" }, 3080Sstevel@tonic-gate { NULL, NULL, NULL } 3090Sstevel@tonic-gate }; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate struct run_single_int_info { 3120Sstevel@tonic-gate uint32_t *rs_out; 3130Sstevel@tonic-gate int rs_result; 3140Sstevel@tonic-gate }; 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate /*ARGSUSED*/ 3170Sstevel@tonic-gate static int 3180Sstevel@tonic-gate run_single_int_callback(void *arg, int columns, char **vals, char **names) 3190Sstevel@tonic-gate { 3200Sstevel@tonic-gate struct run_single_int_info *info = arg; 3210Sstevel@tonic-gate uint32_t val; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate char *endptr = vals[0]; 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate assert(info->rs_result != REP_PROTOCOL_SUCCESS); 3260Sstevel@tonic-gate assert(columns == 1); 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate if (vals[0] == NULL) 3290Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate errno = 0; 3320Sstevel@tonic-gate val = strtoul(vals[0], &endptr, 10); 3330Sstevel@tonic-gate if ((val == 0 && endptr == vals[0]) || *endptr != 0 || errno != 0) 3340Sstevel@tonic-gate backend_panic("malformed integer \"%20s\"", vals[0]); 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate *info->rs_out = val; 3370Sstevel@tonic-gate info->rs_result = REP_PROTOCOL_SUCCESS; 3380Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /*ARGSUSED*/ 3420Sstevel@tonic-gate int 3430Sstevel@tonic-gate backend_fail_if_seen(void *arg, int columns, char **vals, char **names) 3440Sstevel@tonic-gate { 3450Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 3460Sstevel@tonic-gate } 3470Sstevel@tonic-gate 348407Sjwadams /* 349407Sjwadams * check to see if we can successfully start a transaction; if not, the 350407Sjwadams * filesystem is mounted read-only. 351407Sjwadams */ 3520Sstevel@tonic-gate static int 353407Sjwadams backend_is_readonly(struct sqlite *db, const char *path) 3540Sstevel@tonic-gate { 355407Sjwadams int r; 356407Sjwadams statvfs64_t stat; 357407Sjwadams 358407Sjwadams if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY)) 359407Sjwadams return (SQLITE_READONLY); 360407Sjwadams 361407Sjwadams r = sqlite_exec(db, 3620Sstevel@tonic-gate "BEGIN TRANSACTION; " 3630Sstevel@tonic-gate "UPDATE schema_version SET schema_version = schema_version; ", 364407Sjwadams NULL, NULL, NULL); 3650Sstevel@tonic-gate (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 3660Sstevel@tonic-gate return (r); 3670Sstevel@tonic-gate } 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate static void 3700Sstevel@tonic-gate backend_trace_sql(void *arg, const char *sql) 3710Sstevel@tonic-gate { 3720Sstevel@tonic-gate sqlite_backend_t *be = arg; 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate if (backend_print_trace) { 3750Sstevel@tonic-gate (void) fprintf(stderr, "%d: %s\n", be->be_type, sql); 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; 3800Sstevel@tonic-gate static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; 3810Sstevel@tonic-gate 382*7128Samaguire /* 383*7128Samaguire * For a native build, repositories are created from scratch, so upgrade 384*7128Samaguire * is not an issue. This variable is implicitly protected by 385*7128Samaguire * bes[BACKEND_TYPE_NORMAL]->be_lock. 386*7128Samaguire */ 387*7128Samaguire #ifdef NATIVE_BUILD 388*7128Samaguire static boolean_t be_normal_upgraded = B_TRUE; 389*7128Samaguire #else 390*7128Samaguire static boolean_t be_normal_upgraded = B_FALSE; 391*7128Samaguire #endif /* NATIVE_BUILD */ 392*7128Samaguire 393*7128Samaguire /* 394*7128Samaguire * Has backend been upgraded? In nonpersistent case, answer is always 395*7128Samaguire * yes. 396*7128Samaguire */ 397*7128Samaguire boolean_t 398*7128Samaguire backend_is_upgraded(backend_tx_t *bt) 399*7128Samaguire { 400*7128Samaguire if (bt->bt_type == BACKEND_TYPE_NONPERSIST) 401*7128Samaguire return (B_TRUE); 402*7128Samaguire return (be_normal_upgraded); 403*7128Samaguire } 404*7128Samaguire 4050Sstevel@tonic-gate #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) 4060Sstevel@tonic-gate /* 4070Sstevel@tonic-gate * backend_panic() -- some kind of database problem or corruption has been hit. 4080Sstevel@tonic-gate * We attempt to quiesce the other database users -- all of the backend sql 4090Sstevel@tonic-gate * entry points will call backend_panic(NULL) if a panic is in progress, as 4100Sstevel@tonic-gate * will any attempt to start a transaction. 4110Sstevel@tonic-gate * 4120Sstevel@tonic-gate * We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to 4130Sstevel@tonic-gate * either drop the lock or call backend_panic(). If they don't respond in 4140Sstevel@tonic-gate * time, we'll just exit anyway. 4150Sstevel@tonic-gate */ 4160Sstevel@tonic-gate void 4170Sstevel@tonic-gate backend_panic(const char *format, ...) 4180Sstevel@tonic-gate { 4190Sstevel@tonic-gate int i; 4200Sstevel@tonic-gate va_list args; 4210Sstevel@tonic-gate int failed = 0; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 4240Sstevel@tonic-gate if (backend_panic_thread != 0) { 4250Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 4260Sstevel@tonic-gate /* 4270Sstevel@tonic-gate * first, drop any backend locks we're holding, then 4280Sstevel@tonic-gate * sleep forever on the panic_cv. 4290Sstevel@tonic-gate */ 4300Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4310Sstevel@tonic-gate if (bes[i] != NULL && 4320Sstevel@tonic-gate bes[i]->be_thread == pthread_self()) 4330Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 4360Sstevel@tonic-gate for (;;) 4370Sstevel@tonic-gate (void) pthread_cond_wait(&backend_panic_cv, 4380Sstevel@tonic-gate &backend_panic_lock); 4390Sstevel@tonic-gate } 4400Sstevel@tonic-gate backend_panic_thread = pthread_self(); 4410Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4440Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread == pthread_self()) 4450Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate va_start(args, format); 4490Sstevel@tonic-gate configd_vcritical(format, args); 4500Sstevel@tonic-gate va_end(args); 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4530Sstevel@tonic-gate timespec_t rel; 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate rel.tv_sec = 0; 4560Sstevel@tonic-gate rel.tv_nsec = BACKEND_PANIC_TIMEOUT; 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread != pthread_self()) { 4590Sstevel@tonic-gate if (pthread_mutex_reltimedlock_np(&bes[i]->be_lock, 4600Sstevel@tonic-gate &rel) != 0) 4610Sstevel@tonic-gate failed++; 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate if (failed) { 4650Sstevel@tonic-gate configd_critical("unable to quiesce database\n"); 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate if (backend_panic_abort) 4690Sstevel@tonic-gate abort(); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate exit(CONFIGD_EXIT_DATABASE_BAD); 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate /* 4750Sstevel@tonic-gate * Returns 4760Sstevel@tonic-gate * _SUCCESS 4770Sstevel@tonic-gate * _DONE - callback aborted query 4780Sstevel@tonic-gate * _NO_RESOURCES - out of memory (_FULL & _TOOBIG?) 4790Sstevel@tonic-gate */ 4800Sstevel@tonic-gate static int 4810Sstevel@tonic-gate backend_error(sqlite_backend_t *be, int error, char *errmsg) 4820Sstevel@tonic-gate { 4830Sstevel@tonic-gate if (error == SQLITE_OK) 4840Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate switch (error) { 4870Sstevel@tonic-gate case SQLITE_ABORT: 4880Sstevel@tonic-gate free(errmsg); 4890Sstevel@tonic-gate return (REP_PROTOCOL_DONE); 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate case SQLITE_NOMEM: 4920Sstevel@tonic-gate case SQLITE_FULL: 4930Sstevel@tonic-gate case SQLITE_TOOBIG: 4940Sstevel@tonic-gate free(errmsg); 4950Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate default: 4980Sstevel@tonic-gate backend_panic("%s: db error: %s", be->be_path, errmsg); 4990Sstevel@tonic-gate /*NOTREACHED*/ 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate static void 5040Sstevel@tonic-gate backend_backup_cleanup(const char **out_arg, ssize_t out_sz) 5050Sstevel@tonic-gate { 5060Sstevel@tonic-gate char **out = (char **)out_arg; 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate while (out_sz-- > 0) 5090Sstevel@tonic-gate free(*out++); 5100Sstevel@tonic-gate free(out_arg); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate /* 5140Sstevel@tonic-gate * builds a inverse-time-sorted array of backup files. The path is a 5150Sstevel@tonic-gate * a single buffer, and the pointers look like: 5160Sstevel@tonic-gate * 5170Sstevel@tonic-gate * /this/is/a/full/path/to/repository-name-YYYYMMDDHHMMSS 5180Sstevel@tonic-gate * ^pathname ^ ^(pathname+pathlen) 5190Sstevel@tonic-gate * basename 5200Sstevel@tonic-gate * 5210Sstevel@tonic-gate * dirname will either be pathname, or ".". 5220Sstevel@tonic-gate * 5230Sstevel@tonic-gate * Returns the number of elements in the array, 0 if there are no previous 5240Sstevel@tonic-gate * backups, or -1 on error. 5250Sstevel@tonic-gate */ 5260Sstevel@tonic-gate static ssize_t 5270Sstevel@tonic-gate backend_backup_get_prev(char *pathname, size_t pathlen, const char ***out_arg) 5280Sstevel@tonic-gate { 5290Sstevel@tonic-gate char b_start, b_end; 5300Sstevel@tonic-gate DIR *dir; 5310Sstevel@tonic-gate char **out = NULL; 5320Sstevel@tonic-gate char *name, *p; 5330Sstevel@tonic-gate char *dirname, *basename; 5340Sstevel@tonic-gate char *pathend; 5350Sstevel@tonic-gate struct dirent *ent; 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate size_t count = 0; 5380Sstevel@tonic-gate size_t baselen; 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* 5410Sstevel@tonic-gate * year, month, day, hour, min, sec, plus an '_'. 5420Sstevel@tonic-gate */ 5430Sstevel@tonic-gate const size_t ndigits = 4 + 5*2 + 1; 5440Sstevel@tonic-gate const size_t baroffset = 4 + 2*2; 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate size_t idx; 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate pathend = pathname + pathlen; 5490Sstevel@tonic-gate b_end = *pathend; 5500Sstevel@tonic-gate *pathend = '\0'; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate basename = strrchr(pathname, '/'); 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate if (basename != NULL) { 5550Sstevel@tonic-gate assert(pathend > pathname && basename < pathend); 5560Sstevel@tonic-gate basename++; 5570Sstevel@tonic-gate dirname = pathname; 5580Sstevel@tonic-gate } else { 5590Sstevel@tonic-gate basename = pathname; 5600Sstevel@tonic-gate dirname = "."; 5610Sstevel@tonic-gate } 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate baselen = strlen(basename); 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate /* 5660Sstevel@tonic-gate * munge the string temporarily for the opendir(), then restore it. 5670Sstevel@tonic-gate */ 5680Sstevel@tonic-gate b_start = basename[0]; 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate basename[0] = '\0'; 5710Sstevel@tonic-gate dir = opendir(dirname); 5720Sstevel@tonic-gate basename[0] = b_start; /* restore path */ 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate if (dir == NULL) 5750Sstevel@tonic-gate goto fail; 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate while ((ent = readdir(dir)) != NULL) { 5790Sstevel@tonic-gate /* 5800Sstevel@tonic-gate * Must match: 5810Sstevel@tonic-gate * basename-YYYYMMDD_HHMMSS 5820Sstevel@tonic-gate * or we ignore it. 5830Sstevel@tonic-gate */ 5840Sstevel@tonic-gate if (strncmp(ent->d_name, basename, baselen) != 0) 5850Sstevel@tonic-gate continue; 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate name = ent->d_name; 5880Sstevel@tonic-gate if (name[baselen] != '-') 5890Sstevel@tonic-gate continue; 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate p = name + baselen + 1; 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate for (idx = 0; idx < ndigits; idx++) { 5940Sstevel@tonic-gate char c = p[idx]; 5950Sstevel@tonic-gate if (idx == baroffset && c != '_') 5960Sstevel@tonic-gate break; 5970Sstevel@tonic-gate if (idx != baroffset && (c < '0' || c > '9')) 5980Sstevel@tonic-gate break; 5990Sstevel@tonic-gate } 6000Sstevel@tonic-gate if (idx != ndigits || p[idx] != '\0') 6010Sstevel@tonic-gate continue; 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate /* 6040Sstevel@tonic-gate * We have a match. insertion-sort it into our list. 6050Sstevel@tonic-gate */ 6060Sstevel@tonic-gate name = strdup(name); 6070Sstevel@tonic-gate if (name == NULL) 6080Sstevel@tonic-gate goto fail_closedir; 6090Sstevel@tonic-gate p = strrchr(name, '-'); 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 6120Sstevel@tonic-gate char *tmp = out[idx]; 6130Sstevel@tonic-gate char *tp = strrchr(tmp, '-'); 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate int cmp = strcmp(p, tp); 6160Sstevel@tonic-gate if (cmp == 0) 6170Sstevel@tonic-gate cmp = strcmp(name, tmp); 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate if (cmp == 0) { 6200Sstevel@tonic-gate free(name); 6210Sstevel@tonic-gate name = NULL; 6220Sstevel@tonic-gate break; 6230Sstevel@tonic-gate } else if (cmp > 0) { 6240Sstevel@tonic-gate out[idx] = name; 6250Sstevel@tonic-gate name = tmp; 6260Sstevel@tonic-gate p = tp; 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate if (idx == count) { 6310Sstevel@tonic-gate char **new_out = realloc(out, 6320Sstevel@tonic-gate (count + 1) * sizeof (*out)); 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate if (new_out == NULL) { 6350Sstevel@tonic-gate free(name); 6360Sstevel@tonic-gate goto fail_closedir; 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate out = new_out; 6400Sstevel@tonic-gate out[count++] = name; 6410Sstevel@tonic-gate } else { 6420Sstevel@tonic-gate assert(name == NULL); 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate (void) closedir(dir); 6460Sstevel@tonic-gate 6470Sstevel@tonic-gate basename[baselen] = b_end; 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate *out_arg = (const char **)out; 6500Sstevel@tonic-gate return (count); 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate fail_closedir: 6530Sstevel@tonic-gate (void) closedir(dir); 6540Sstevel@tonic-gate fail: 6550Sstevel@tonic-gate basename[0] = b_start; 6560Sstevel@tonic-gate *pathend = b_end; 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate backend_backup_cleanup((const char **)out, count); 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate *out_arg = NULL; 6610Sstevel@tonic-gate return (-1); 6620Sstevel@tonic-gate } 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate /* 6650Sstevel@tonic-gate * Copies the repository path into out, a buffer of out_len bytes, 6660Sstevel@tonic-gate * removes the ".db" (or whatever) extension, and, if name is non-NULL, 6670Sstevel@tonic-gate * appends "-name" to it. If name is non-NULL, it can fail with: 6680Sstevel@tonic-gate * 6690Sstevel@tonic-gate * _TRUNCATED will not fit in buffer. 6700Sstevel@tonic-gate * _BAD_REQUEST name is not a valid identifier 6710Sstevel@tonic-gate */ 6720Sstevel@tonic-gate static rep_protocol_responseid_t 6730Sstevel@tonic-gate backend_backup_base(sqlite_backend_t *be, const char *name, 6740Sstevel@tonic-gate char *out, size_t out_len) 6750Sstevel@tonic-gate { 6760Sstevel@tonic-gate char *p, *q; 6770Sstevel@tonic-gate size_t len; 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate /* 6800Sstevel@tonic-gate * for paths of the form /path/to/foo.db, we truncate at the final 6810Sstevel@tonic-gate * '.'. 6820Sstevel@tonic-gate */ 6830Sstevel@tonic-gate (void) strlcpy(out, be->be_path, out_len); 6840Sstevel@tonic-gate 6850Sstevel@tonic-gate p = strrchr(out, '/'); 6860Sstevel@tonic-gate q = strrchr(out, '.'); 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate if (p != NULL && q != NULL && q > p) 6890Sstevel@tonic-gate *q = 0; 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate if (name != NULL) { 6920Sstevel@tonic-gate len = strlen(out); 6930Sstevel@tonic-gate assert(len < out_len); 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate out += len; 6960Sstevel@tonic-gate out_len -= len; 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate len = strlen(name); 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate /* 7010Sstevel@tonic-gate * verify that the name tag is entirely alphabetic, 7020Sstevel@tonic-gate * non-empty, and not too long. 7030Sstevel@tonic-gate */ 7040Sstevel@tonic-gate if (len == 0 || len >= REP_PROTOCOL_NAME_LEN || 7050Sstevel@tonic-gate uu_check_name(name, UU_NAME_DOMAIN) < 0) 7060Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate if (snprintf(out, out_len, "-%s", name) >= out_len) 7090Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 7100Sstevel@tonic-gate } 7110Sstevel@tonic-gate 7120Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 7130Sstevel@tonic-gate } 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate /* 716407Sjwadams * See if a backup is needed. We do a backup unless both files are 717407Sjwadams * byte-for-byte identical. 718407Sjwadams */ 719407Sjwadams static int 720407Sjwadams backend_check_backup_needed(const char *rep_name, const char *backup_name) 721407Sjwadams { 722407Sjwadams int repfd = open(rep_name, O_RDONLY); 723407Sjwadams int fd = open(backup_name, O_RDONLY); 724407Sjwadams struct stat s_rep, s_backup; 725407Sjwadams int c1, c2; 726407Sjwadams 727407Sjwadams FILE *f_rep = NULL; 728407Sjwadams FILE *f_backup = NULL; 729407Sjwadams 730407Sjwadams if (repfd < 0 || fd < 0) 731407Sjwadams goto fail; 732407Sjwadams 733407Sjwadams if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0) 734407Sjwadams goto fail; 735407Sjwadams 736407Sjwadams /* 737407Sjwadams * if they are the same file, we need to do a backup to break the 738407Sjwadams * hard link or symlink involved. 739407Sjwadams */ 740407Sjwadams if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev) 741407Sjwadams goto fail; 742407Sjwadams 743407Sjwadams if (s_rep.st_size != s_backup.st_size) 744407Sjwadams goto fail; 745407Sjwadams 746407Sjwadams if ((f_rep = fdopen(repfd, "r")) == NULL || 747407Sjwadams (f_backup = fdopen(fd, "r")) == NULL) 748407Sjwadams goto fail; 749407Sjwadams 750407Sjwadams do { 751407Sjwadams c1 = getc(f_rep); 752407Sjwadams c2 = getc(f_backup); 753407Sjwadams if (c1 != c2) 754407Sjwadams goto fail; 755407Sjwadams } while (c1 != EOF); 756407Sjwadams 757407Sjwadams if (!ferror(f_rep) && !ferror(f_backup)) { 758407Sjwadams (void) fclose(f_rep); 759407Sjwadams (void) fclose(f_backup); 760407Sjwadams (void) close(repfd); 761407Sjwadams (void) close(fd); 762407Sjwadams return (0); 763407Sjwadams } 764407Sjwadams 765407Sjwadams fail: 766407Sjwadams if (f_rep != NULL) 767407Sjwadams (void) fclose(f_rep); 768407Sjwadams if (f_backup != NULL) 769407Sjwadams (void) fclose(f_backup); 770407Sjwadams if (repfd >= 0) 771407Sjwadams (void) close(repfd); 772407Sjwadams if (fd >= 0) 773407Sjwadams (void) close(fd); 774407Sjwadams return (1); 775407Sjwadams } 776407Sjwadams 777407Sjwadams /* 7786035Sstevep * This interface is called to perform the actual copy 7796035Sstevep * 7806035Sstevep * Return: 7816035Sstevep * _FAIL_UNKNOWN read/write fails 7826035Sstevep * _FAIL_NO_RESOURCES out of memory 7836035Sstevep * _SUCCESS copy succeeds 7846035Sstevep */ 7856035Sstevep static rep_protocol_responseid_t 7866035Sstevep backend_do_copy(const char *src, int srcfd, const char *dst, 7876035Sstevep int dstfd, size_t *sz) 7886035Sstevep { 7896035Sstevep char *buf; 7906035Sstevep off_t nrd, nwr, n, r_off = 0, w_off = 0; 7916035Sstevep 7926035Sstevep if ((buf = malloc(8192)) == NULL) 7936035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 7946035Sstevep 7956035Sstevep while ((nrd = read(srcfd, buf, 8192)) != 0) { 7966035Sstevep if (nrd < 0) { 7976035Sstevep if (errno == EINTR) 7986035Sstevep continue; 7996035Sstevep 8006035Sstevep configd_critical( 8016035Sstevep "Backend copy failed: fails to read from %s " 8026035Sstevep "at offset %d: %s\n", src, r_off, strerror(errno)); 8036035Sstevep free(buf); 8046035Sstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 8056035Sstevep } 8066035Sstevep 8076035Sstevep r_off += nrd; 8086035Sstevep 8096035Sstevep nwr = 0; 8106035Sstevep do { 8116035Sstevep if ((n = write(dstfd, &buf[nwr], nrd - nwr)) < 0) { 8126035Sstevep if (errno == EINTR) 8136035Sstevep continue; 8146035Sstevep 8156035Sstevep configd_critical( 8166035Sstevep "Backend copy failed: fails to write to %s " 8176035Sstevep "at offset %d: %s\n", dst, w_off, 8186035Sstevep strerror(errno)); 8196035Sstevep free(buf); 8206035Sstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 8216035Sstevep } 8226035Sstevep 8236035Sstevep nwr += n; 8246035Sstevep w_off += n; 8256035Sstevep 8266035Sstevep } while (nwr < nrd); 8276035Sstevep } 8286035Sstevep 8296035Sstevep if (sz) 8306035Sstevep *sz = w_off; 8316035Sstevep 8326035Sstevep free(buf); 8336035Sstevep return (REP_PROTOCOL_SUCCESS); 8346035Sstevep } 8356035Sstevep 8366035Sstevep /* 8370Sstevel@tonic-gate * Can return: 8380Sstevel@tonic-gate * _BAD_REQUEST name is not valid 8390Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 8400Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 8410Sstevel@tonic-gate * console) 8420Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 8436035Sstevep * _NO_RESOURCES out of memory 8440Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 8450Sstevel@tonic-gate */ 8460Sstevel@tonic-gate static rep_protocol_responseid_t 8470Sstevel@tonic-gate backend_create_backup_locked(sqlite_backend_t *be, const char *name) 8480Sstevel@tonic-gate { 8490Sstevel@tonic-gate const char **old_list; 8500Sstevel@tonic-gate ssize_t old_sz; 8510Sstevel@tonic-gate ssize_t old_max = max_repository_backups; 8520Sstevel@tonic-gate ssize_t cur; 8530Sstevel@tonic-gate char *finalname; 8546035Sstevep char *finalpath; 8556035Sstevep char *tmppath; 8560Sstevel@tonic-gate int infd, outfd; 8570Sstevel@tonic-gate size_t len; 8580Sstevel@tonic-gate time_t now; 8590Sstevel@tonic-gate struct tm now_tm; 8600Sstevel@tonic-gate rep_protocol_responseid_t result; 8610Sstevel@tonic-gate 8626035Sstevep if ((finalpath = malloc(PATH_MAX)) == NULL) 8636035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 8646035Sstevep 8656035Sstevep if ((tmppath = malloc(PATH_MAX)) == NULL) { 8666035Sstevep free(finalpath); 8676035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 8686035Sstevep } 8690Sstevel@tonic-gate 8706035Sstevep if (be->be_readonly) { 8716035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_READONLY; 8726035Sstevep goto out; 8736035Sstevep } 8746035Sstevep 8756035Sstevep result = backend_backup_base(be, name, finalpath, PATH_MAX); 8760Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 8776035Sstevep goto out; 8780Sstevel@tonic-gate 879407Sjwadams if (!backend_check_backup_needed(be->be_path, finalpath)) { 8806035Sstevep result = REP_PROTOCOL_SUCCESS; 8816035Sstevep goto out; 882407Sjwadams } 883407Sjwadams 8840Sstevel@tonic-gate /* 8850Sstevel@tonic-gate * remember the original length, and the basename location 8860Sstevel@tonic-gate */ 8870Sstevel@tonic-gate len = strlen(finalpath); 8880Sstevel@tonic-gate finalname = strrchr(finalpath, '/'); 8890Sstevel@tonic-gate if (finalname != NULL) 8900Sstevel@tonic-gate finalname++; 8910Sstevel@tonic-gate else 8920Sstevel@tonic-gate finalname = finalpath; 8930Sstevel@tonic-gate 8946035Sstevep (void) strlcpy(tmppath, finalpath, PATH_MAX); 8956035Sstevep if (strlcat(tmppath, "-tmpXXXXXX", PATH_MAX) >= PATH_MAX) { 8966035Sstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 8976035Sstevep goto out; 8986035Sstevep } 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate now = time(NULL); 9010Sstevel@tonic-gate if (localtime_r(&now, &now_tm) == NULL) { 9020Sstevel@tonic-gate configd_critical( 9030Sstevel@tonic-gate "\"%s\" backup failed: localtime(3C) failed: %s\n", name, 9040Sstevel@tonic-gate be->be_path, strerror(errno)); 9056035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 9066035Sstevep goto out; 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate 9096035Sstevep if (strftime(finalpath + len, PATH_MAX - len, 9106035Sstevep "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= PATH_MAX - len) { 9116035Sstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 9126035Sstevep goto out; 9130Sstevel@tonic-gate } 9140Sstevel@tonic-gate 9150Sstevel@tonic-gate infd = open(be->be_path, O_RDONLY); 9160Sstevel@tonic-gate if (infd < 0) { 9170Sstevel@tonic-gate configd_critical("\"%s\" backup failed: opening %s: %s\n", name, 9180Sstevel@tonic-gate be->be_path, strerror(errno)); 9196035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 9206035Sstevep goto out; 9210Sstevel@tonic-gate } 9220Sstevel@tonic-gate 9230Sstevel@tonic-gate outfd = mkstemp(tmppath); 9240Sstevel@tonic-gate if (outfd < 0) { 9250Sstevel@tonic-gate configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n", 9260Sstevel@tonic-gate name, tmppath, strerror(errno)); 9270Sstevel@tonic-gate (void) close(infd); 9286035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 9296035Sstevep goto out; 9300Sstevel@tonic-gate } 9310Sstevel@tonic-gate 9326035Sstevep if ((result = backend_do_copy((const char *)be->be_path, infd, 9336035Sstevep (const char *)tmppath, outfd, NULL)) != REP_PROTOCOL_SUCCESS) 9340Sstevel@tonic-gate goto fail; 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate /* 9370Sstevel@tonic-gate * grab the old list before doing our re-name. 9380Sstevel@tonic-gate */ 9390Sstevel@tonic-gate if (old_max > 0) 9400Sstevel@tonic-gate old_sz = backend_backup_get_prev(finalpath, len, &old_list); 9410Sstevel@tonic-gate 9420Sstevel@tonic-gate if (rename(tmppath, finalpath) < 0) { 9430Sstevel@tonic-gate configd_critical( 9440Sstevel@tonic-gate "\"%s\" backup failed: rename(%s, %s): %s\n", 9450Sstevel@tonic-gate name, tmppath, finalpath, strerror(errno)); 9460Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 9470Sstevel@tonic-gate goto fail; 9480Sstevel@tonic-gate } 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate tmppath[len] = 0; /* strip -XXXXXX, for reference symlink */ 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate (void) unlink(tmppath); 9530Sstevel@tonic-gate if (symlink(finalname, tmppath) < 0) { 9540Sstevel@tonic-gate configd_critical( 9550Sstevel@tonic-gate "\"%s\" backup completed, but updating " 9560Sstevel@tonic-gate "\"%s\" symlink to \"%s\" failed: %s\n", 9570Sstevel@tonic-gate name, tmppath, finalname, strerror(errno)); 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate if (old_max > 0 && old_sz > 0) { 9610Sstevel@tonic-gate /* unlink all but the first (old_max - 1) files */ 9620Sstevel@tonic-gate for (cur = old_max - 1; cur < old_sz; cur++) { 9630Sstevel@tonic-gate (void) strlcpy(finalname, old_list[cur], 9646035Sstevep PATH_MAX - (finalname - finalpath)); 9650Sstevel@tonic-gate if (unlink(finalpath) < 0) 9660Sstevel@tonic-gate configd_critical( 9670Sstevel@tonic-gate "\"%s\" backup completed, but removing old " 9680Sstevel@tonic-gate "file \"%s\" failed: %s\n", 9690Sstevel@tonic-gate name, finalpath, strerror(errno)); 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate backend_backup_cleanup(old_list, old_sz); 9730Sstevel@tonic-gate } 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate result = REP_PROTOCOL_SUCCESS; 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate fail: 9780Sstevel@tonic-gate (void) close(infd); 9790Sstevel@tonic-gate (void) close(outfd); 9800Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 9810Sstevel@tonic-gate (void) unlink(tmppath); 9820Sstevel@tonic-gate 9836035Sstevep out: 9846035Sstevep free(finalpath); 9856035Sstevep free(tmppath); 9866035Sstevep 9870Sstevel@tonic-gate return (result); 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate 990*7128Samaguire /* 991*7128Samaguire * Check if value_tbl has been upgraded in the main database, and 992*7128Samaguire * if not (if the value_order column is not present), and do_upgrade is true, 993*7128Samaguire * upgrade value_tbl in repository to contain the additional value_order 994*7128Samaguire * column. The version of sqlite used means ALTER TABLE is not 995*7128Samaguire * available, so we cannot simply use "ALTER TABLE value_tbl ADD COLUMN". 996*7128Samaguire * Rather we need to create a temporary table with the additional column, 997*7128Samaguire * import the value_tbl, drop the original value_tbl, recreate the value_tbl 998*7128Samaguire * with the additional column, import the values from value_tbl_tmp, 999*7128Samaguire * reindex and finally drop value_tbl_tmp. During boot, we wish to check 1000*7128Samaguire * if the repository has been upgraded before it is writable, so that 1001*7128Samaguire * property value retrieval can use the appropriate form of the SELECT 1002*7128Samaguire * statement that retrieves property values. As a result, we need to check 1003*7128Samaguire * if the repository has been upgraded prior to the point when we can 1004*7128Samaguire * actually carry out the update. 1005*7128Samaguire */ 1006*7128Samaguire void 1007*7128Samaguire backend_check_upgrade(sqlite_backend_t *be, boolean_t do_upgrade) 1008*7128Samaguire { 1009*7128Samaguire char *errp; 1010*7128Samaguire int r; 1011*7128Samaguire 1012*7128Samaguire if (be_normal_upgraded) 1013*7128Samaguire return; 1014*7128Samaguire /* 1015*7128Samaguire * Test if upgrade is needed. If value_order column does not exist, 1016*7128Samaguire * we need to upgrade the schema. 1017*7128Samaguire */ 1018*7128Samaguire r = sqlite_exec(be->be_db, "SELECT value_order FROM value_tbl LIMIT 1;", 1019*7128Samaguire NULL, NULL, NULL); 1020*7128Samaguire if (r == SQLITE_ERROR && do_upgrade) { 1021*7128Samaguire /* No value_order column - needs upgrade */ 1022*7128Samaguire configd_info("Upgrading SMF repository format..."); 1023*7128Samaguire r = sqlite_exec(be->be_db, 1024*7128Samaguire "BEGIN TRANSACTION; " 1025*7128Samaguire "CREATE TABLE value_tbl_tmp ( " 1026*7128Samaguire "value_id INTEGER NOT NULL, " 1027*7128Samaguire "value_type CHAR(1) NOT NULL, " 1028*7128Samaguire "value_value VARCHAR NOT NULL, " 1029*7128Samaguire "value_order INTEGER DEFAULT 0); " 1030*7128Samaguire "INSERT INTO value_tbl_tmp " 1031*7128Samaguire "(value_id, value_type, value_value) " 1032*7128Samaguire "SELECT value_id, value_type, value_value FROM value_tbl; " 1033*7128Samaguire "DROP TABLE value_tbl; " 1034*7128Samaguire "CREATE TABLE value_tbl( " 1035*7128Samaguire "value_id INTEGER NOT NULL, " 1036*7128Samaguire "value_type CHAR(1) NOT NULL, " 1037*7128Samaguire "value_value VARCHAR NOT NULL, " 1038*7128Samaguire "value_order INTEGER DEFAULT 0); " 1039*7128Samaguire "INSERT INTO value_tbl SELECT * FROM value_tbl_tmp; " 1040*7128Samaguire "CREATE INDEX value_tbl_id ON value_tbl (value_id); " 1041*7128Samaguire "DROP TABLE value_tbl_tmp; " 1042*7128Samaguire "COMMIT TRANSACTION; " 1043*7128Samaguire "VACUUM; ", 1044*7128Samaguire NULL, NULL, &errp); 1045*7128Samaguire if (r == SQLITE_OK) { 1046*7128Samaguire configd_info("SMF repository upgrade is complete."); 1047*7128Samaguire } else { 1048*7128Samaguire backend_panic("%s: repository upgrade failed: %s", 1049*7128Samaguire be->be_path, errp); 1050*7128Samaguire /* NOTREACHED */ 1051*7128Samaguire } 1052*7128Samaguire } 1053*7128Samaguire if (r == SQLITE_OK) 1054*7128Samaguire be_normal_upgraded = B_TRUE; 1055*7128Samaguire else 1056*7128Samaguire be_normal_upgraded = B_FALSE; 1057*7128Samaguire } 1058*7128Samaguire 1059407Sjwadams static int 1060407Sjwadams backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) 1061407Sjwadams { 1062407Sjwadams char *errp; 1063407Sjwadams struct sqlite *new; 1064407Sjwadams int r; 1065407Sjwadams 1066407Sjwadams assert(be->be_readonly); 1067407Sjwadams assert(be == bes[BACKEND_TYPE_NORMAL]); 1068407Sjwadams 1069407Sjwadams /* 1070407Sjwadams * If we don't *need* to be writable, only check every once in a 1071407Sjwadams * while. 1072407Sjwadams */ 1073407Sjwadams if (!writing) { 1074407Sjwadams if ((uint64_t)(t - be->be_lastcheck) < 1075407Sjwadams BACKEND_READONLY_CHECK_INTERVAL) 1076407Sjwadams return (REP_PROTOCOL_SUCCESS); 1077407Sjwadams be->be_lastcheck = t; 1078407Sjwadams } 1079407Sjwadams 1080407Sjwadams new = sqlite_open(be->be_path, 0600, &errp); 1081407Sjwadams if (new == NULL) { 1082407Sjwadams backend_panic("reopening %s: %s\n", be->be_path, errp); 1083407Sjwadams /*NOTREACHED*/ 1084407Sjwadams } 1085407Sjwadams r = backend_is_readonly(new, be->be_path); 1086407Sjwadams 1087407Sjwadams if (r != SQLITE_OK) { 1088407Sjwadams sqlite_close(new); 1089407Sjwadams if (writing) 1090407Sjwadams return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 1091407Sjwadams return (REP_PROTOCOL_SUCCESS); 1092407Sjwadams } 1093407Sjwadams 1094407Sjwadams /* 1095407Sjwadams * We can write! Swap the db handles, mark ourself writable, 1096*7128Samaguire * upgrade if necessary, and make a backup. 1097407Sjwadams */ 1098407Sjwadams sqlite_close(be->be_db); 1099407Sjwadams be->be_db = new; 1100407Sjwadams be->be_readonly = 0; 1101407Sjwadams 1102*7128Samaguire if (be->be_type == BACKEND_TYPE_NORMAL) 1103*7128Samaguire backend_check_upgrade(be, B_TRUE); 1104*7128Samaguire 1105407Sjwadams if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != 1106407Sjwadams REP_PROTOCOL_SUCCESS) { 1107407Sjwadams configd_critical( 1108407Sjwadams "unable to create \"%s\" backup of \"%s\"\n", 1109407Sjwadams REPOSITORY_BOOT_BACKUP, be->be_path); 1110407Sjwadams } 1111407Sjwadams 1112407Sjwadams return (REP_PROTOCOL_SUCCESS); 1113407Sjwadams } 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate /* 11160Sstevel@tonic-gate * If t is not BACKEND_TYPE_NORMAL, can fail with 11170Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 11180Sstevel@tonic-gate * 11190Sstevel@tonic-gate * If writing is nonzero, can also fail with 11200Sstevel@tonic-gate * _BACKEND_READONLY - backend is read-only 11210Sstevel@tonic-gate */ 11220Sstevel@tonic-gate static int 11230Sstevel@tonic-gate backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) 11240Sstevel@tonic-gate { 11250Sstevel@tonic-gate sqlite_backend_t *be = NULL; 11260Sstevel@tonic-gate hrtime_t ts, vts; 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate *bep = NULL; 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || 11310Sstevel@tonic-gate t == BACKEND_TYPE_NONPERSIST); 11320Sstevel@tonic-gate 11330Sstevel@tonic-gate be = bes[t]; 11340Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) 11350Sstevel@tonic-gate assert(be != NULL); /* should always be there */ 11360Sstevel@tonic-gate 11370Sstevel@tonic-gate if (be == NULL) 11380Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_ACCESS); 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate if (backend_panic_thread != 0) 11410Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate ts = gethrtime(); 11440Sstevel@tonic-gate vts = gethrvtime(); 11450Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 11460Sstevel@tonic-gate UPDATE_TOTALS_WR(be, writing, bt_lock, ts, vts); 11470Sstevel@tonic-gate 11480Sstevel@tonic-gate if (backend_panic_thread != 0) { 11490Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 11500Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate be->be_thread = pthread_self(); 11530Sstevel@tonic-gate 1154407Sjwadams if (be->be_readonly) { 11550Sstevel@tonic-gate int r; 11560Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL); 11570Sstevel@tonic-gate 1158407Sjwadams r = backend_check_readonly(be, writing, ts); 1159407Sjwadams if (r != REP_PROTOCOL_SUCCESS) { 11600Sstevel@tonic-gate be->be_thread = 0; 11610Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 1162407Sjwadams return (r); 11630Sstevel@tonic-gate } 1164407Sjwadams } 11650Sstevel@tonic-gate 11660Sstevel@tonic-gate if (backend_do_trace) 11670Sstevel@tonic-gate (void) sqlite_trace(be->be_db, backend_trace_sql, be); 11680Sstevel@tonic-gate else 11690Sstevel@tonic-gate (void) sqlite_trace(be->be_db, NULL, NULL); 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate be->be_writing = writing; 11720Sstevel@tonic-gate *bep = be; 11730Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 11740Sstevel@tonic-gate } 11750Sstevel@tonic-gate 11760Sstevel@tonic-gate static void 11770Sstevel@tonic-gate backend_unlock(sqlite_backend_t *be) 11780Sstevel@tonic-gate { 11790Sstevel@tonic-gate be->be_writing = 0; 11800Sstevel@tonic-gate be->be_thread = 0; 11810Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate static void 11850Sstevel@tonic-gate backend_destroy(sqlite_backend_t *be) 11860Sstevel@tonic-gate { 11870Sstevel@tonic-gate if (be->be_db != NULL) { 11880Sstevel@tonic-gate sqlite_close(be->be_db); 11890Sstevel@tonic-gate be->be_db = NULL; 11900Sstevel@tonic-gate } 11910Sstevel@tonic-gate be->be_thread = 0; 11920Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 11930Sstevel@tonic-gate (void) pthread_mutex_destroy(&be->be_lock); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate static void 11970Sstevel@tonic-gate backend_create_finish(backend_type_t backend_id, sqlite_backend_t *be) 11980Sstevel@tonic-gate { 11990Sstevel@tonic-gate assert(MUTEX_HELD(&be->be_lock)); 12000Sstevel@tonic-gate assert(be == &be_info[backend_id]); 12010Sstevel@tonic-gate 12020Sstevel@tonic-gate bes[backend_id] = be; 12030Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 12040Sstevel@tonic-gate } 12050Sstevel@tonic-gate 12060Sstevel@tonic-gate static int 12070Sstevel@tonic-gate backend_fd_write(int fd, const char *mess) 12080Sstevel@tonic-gate { 12090Sstevel@tonic-gate int len = strlen(mess); 12100Sstevel@tonic-gate int written; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate while (len > 0) { 12130Sstevel@tonic-gate if ((written = write(fd, mess, len)) < 0) 12140Sstevel@tonic-gate return (-1); 12150Sstevel@tonic-gate mess += written; 12160Sstevel@tonic-gate len -= written; 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate return (0); 12190Sstevel@tonic-gate } 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate /* 12220Sstevel@tonic-gate * Can return: 12230Sstevel@tonic-gate * _BAD_REQUEST name is not valid 12240Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 12250Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 12260Sstevel@tonic-gate * console) 12270Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 12286035Sstevep * _NO_RESOURCES out of memory 12290Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 12300Sstevel@tonic-gate */ 12310Sstevel@tonic-gate rep_protocol_responseid_t 12320Sstevel@tonic-gate backend_create_backup(const char *name) 12330Sstevel@tonic-gate { 12340Sstevel@tonic-gate rep_protocol_responseid_t result; 12350Sstevel@tonic-gate sqlite_backend_t *be; 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be); 12386035Sstevep assert(result == REP_PROTOCOL_SUCCESS); 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate result = backend_create_backup_locked(be, name); 12410Sstevel@tonic-gate backend_unlock(be); 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate return (result); 12440Sstevel@tonic-gate } 12450Sstevel@tonic-gate 12466035Sstevep /* 12476035Sstevep * Copy the repository. If the sw_back flag is not set, we are 12486035Sstevep * copying the repository from the default location under /etc/svc to 12496035Sstevep * the tmpfs /etc/svc/volatile location. If the flag is set, we are 12506035Sstevep * copying back to the /etc/svc location from the volatile location 12516035Sstevep * after manifest-import is completed. 12526035Sstevep * 12536035Sstevep * Can return: 12546035Sstevep * 12556035Sstevep * REP_PROTOCOL_SUCCESS successful copy and rename 12566035Sstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 12576035Sstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 12586035Sstevep */ 12596035Sstevep static rep_protocol_responseid_t 12606035Sstevep backend_switch_copy(const char *src, const char *dst, int sw_back) 12616035Sstevep { 12626035Sstevep int srcfd, dstfd; 12636035Sstevep char *tmppath = malloc(PATH_MAX); 12646035Sstevep rep_protocol_responseid_t res = REP_PROTOCOL_SUCCESS; 12656035Sstevep struct stat s_buf; 12666035Sstevep size_t cpsz, sz; 12676035Sstevep 12686035Sstevep if (tmppath == NULL) { 12696035Sstevep res = REP_PROTOCOL_FAIL_NO_RESOURCES; 12706035Sstevep goto out; 12716035Sstevep } 12726035Sstevep 12736035Sstevep /* 12746035Sstevep * Create and open the related db files 12756035Sstevep */ 12766035Sstevep (void) strlcpy(tmppath, dst, PATH_MAX); 12776035Sstevep sz = strlcat(tmppath, "-XXXXXX", PATH_MAX); 12786035Sstevep assert(sz < PATH_MAX); 12796035Sstevep if (sz >= PATH_MAX) { 12806035Sstevep configd_critical( 12816035Sstevep "Backend copy failed: strlcat %s: overflow\n", tmppath); 12826035Sstevep abort(); 12836035Sstevep } 12846035Sstevep 12856035Sstevep if ((dstfd = mkstemp(tmppath)) < 0) { 12866035Sstevep configd_critical("Backend copy failed: mkstemp %s: %s\n", 12876035Sstevep tmppath, strerror(errno)); 12886035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 12896035Sstevep goto out; 12906035Sstevep } 12916035Sstevep 12926035Sstevep if ((srcfd = open(src, O_RDONLY)) < 0) { 12936035Sstevep configd_critical("Backend copy failed: opening %s: %s\n", 12946035Sstevep src, strerror(errno)); 12956035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 12966035Sstevep goto errexit; 12976035Sstevep } 12986035Sstevep 12996035Sstevep /* 13006035Sstevep * fstat the backend before copy for sanity check. 13016035Sstevep */ 13026035Sstevep if (fstat(srcfd, &s_buf) < 0) { 13036035Sstevep configd_critical("Backend copy failed: fstat %s: %s\n", 13046035Sstevep src, strerror(errno)); 13056035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 13066035Sstevep goto errexit; 13076035Sstevep } 13086035Sstevep 13096035Sstevep if ((res = backend_do_copy(src, srcfd, dst, dstfd, &cpsz)) != 13106035Sstevep REP_PROTOCOL_SUCCESS) 13116035Sstevep goto errexit; 13126035Sstevep 13136035Sstevep if (cpsz != s_buf.st_size) { 13146035Sstevep configd_critical("Backend copy failed: incomplete copy\n"); 13156035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 13166035Sstevep goto errexit; 13176035Sstevep } 13186035Sstevep 13196035Sstevep /* 13206035Sstevep * Rename tmppath to dst 13216035Sstevep */ 13226035Sstevep if (rename(tmppath, dst) < 0) { 13236035Sstevep configd_critical( 13246035Sstevep "Backend copy failed: rename %s to %s: %s\n", 13256035Sstevep tmppath, dst, strerror(errno)); 13266035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 13276035Sstevep } 13286035Sstevep 13296035Sstevep errexit: 13306035Sstevep if (res != REP_PROTOCOL_SUCCESS && unlink(tmppath) < 0) 13316035Sstevep configd_critical( 13326035Sstevep "Backend copy failed: remove %s: %s\n", 13336035Sstevep tmppath, strerror(errno)); 13346035Sstevep 13356035Sstevep (void) close(srcfd); 13366035Sstevep (void) close(dstfd); 13376035Sstevep 13386035Sstevep out: 13396035Sstevep free(tmppath); 13406035Sstevep if (sw_back) { 13416035Sstevep if (unlink(src) < 0) 13426035Sstevep configd_critical( 13436035Sstevep "Backend copy failed: remove %s: %s\n", 13446035Sstevep src, strerror(errno)); 13456035Sstevep } 13466035Sstevep 13476035Sstevep return (res); 13486035Sstevep } 13496035Sstevep 13506035Sstevep /* 13516035Sstevep * Perform sanity check on the repository. 13526035Sstevep * Return 0 if check succeeds or -1 if fails. 13536035Sstevep */ 13546035Sstevep static int 13556035Sstevep backend_switch_check(struct sqlite *be_db, char **errp) 13566035Sstevep { 13576035Sstevep struct run_single_int_info info; 13586035Sstevep uint32_t val = -1UL; 13596035Sstevep int r; 13606035Sstevep 13616035Sstevep info.rs_out = &val; 13626035Sstevep info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 13636035Sstevep 13646035Sstevep r = sqlite_exec(be_db, 13656035Sstevep "SELECT schema_version FROM schema_version;", 13666035Sstevep run_single_int_callback, &info, errp); 13676035Sstevep 13686035Sstevep if (r == SQLITE_OK && 13696035Sstevep info.rs_result != REP_PROTOCOL_FAIL_NOT_FOUND && 13706035Sstevep val == BACKEND_SCHEMA_VERSION) 13716035Sstevep return (0); 13726035Sstevep else 13736035Sstevep return (-1); 13746035Sstevep } 13756035Sstevep 13766035Sstevep /* 13776035Sstevep * Backend switch entry point. It is called to perform the backend copy and 13786035Sstevep * switch from src to dst. First, it blocks all other clients from accessing 13796035Sstevep * the repository by calling backend_lock to lock the repository. Upon 13806035Sstevep * successful lock, copying and switching of the repository are performed. 13816035Sstevep * 13826035Sstevep * Can return: 13836035Sstevep * REP_PROTOCOL_SUCCESS successful switch 13846035Sstevep * REP_PROTOCOL_FAIL_BACKEND_ACCESS backen access fails 13856035Sstevep * REP_PROTOCOL_FAIL_BACKEND_READONLY backend is not writable 13866035Sstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 13876035Sstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 13886035Sstevep */ 13896035Sstevep rep_protocol_responseid_t 13906035Sstevep backend_switch(int sw_back) 13916035Sstevep { 13926035Sstevep rep_protocol_responseid_t result; 13936035Sstevep sqlite_backend_t *be; 13946035Sstevep struct sqlite *new; 13956035Sstevep char *errp; 13966035Sstevep const char *dst; 13976035Sstevep 13986035Sstevep result = backend_lock(BACKEND_TYPE_NORMAL, 1, &be); 13996035Sstevep if (result != REP_PROTOCOL_SUCCESS) 14006035Sstevep return (result); 14016035Sstevep 14026035Sstevep if (sw_back) { 14036035Sstevep dst = REPOSITORY_DB; 14046035Sstevep } else { 14056035Sstevep dst = FAST_REPOSITORY_DB; 14066035Sstevep } 14076035Sstevep 14086035Sstevep /* 14096035Sstevep * Do the actual copy and rename 14106035Sstevep */ 14116035Sstevep result = backend_switch_copy(be->be_path, dst, sw_back); 14126035Sstevep if (result != REP_PROTOCOL_SUCCESS) { 14136035Sstevep goto errout; 14146035Sstevep } 14156035Sstevep 14166035Sstevep /* 14176035Sstevep * Do the backend sanity check and switch 14186035Sstevep */ 14196035Sstevep new = sqlite_open(dst, 0600, &errp); 14206035Sstevep if (new != NULL) { 14216035Sstevep /* 14226035Sstevep * Sanity check 14236035Sstevep */ 14246035Sstevep if (backend_switch_check(new, &errp) == 0) { 14256035Sstevep free((char *)be->be_path); 14266035Sstevep be->be_path = strdup(dst); 14276035Sstevep if (be->be_path == NULL) { 14286035Sstevep configd_critical( 14296035Sstevep "Backend switch failed: strdup %s: %s\n", 14306035Sstevep dst, strerror(errno)); 14316035Sstevep result = REP_PROTOCOL_FAIL_NO_RESOURCES; 14326035Sstevep sqlite_close(new); 14336035Sstevep } else { 14346035Sstevep sqlite_close(be->be_db); 14356035Sstevep be->be_db = new; 14366035Sstevep } 14376035Sstevep } else { 14386035Sstevep configd_critical( 14396035Sstevep "Backend switch failed: integrity check %s: %s\n", 14406035Sstevep dst, errp); 14416035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 14426035Sstevep } 14436035Sstevep } else { 14446035Sstevep configd_critical("Backend switch failed: sqlite_open %s: %s\n", 14456035Sstevep dst, errp); 14466035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 14476035Sstevep } 14486035Sstevep 14496035Sstevep errout: 14506035Sstevep backend_unlock(be); 14516035Sstevep return (result); 14526035Sstevep } 14536035Sstevep 14546035Sstevep /* 14556035Sstevep * This routine is called to attempt the recovery of 14566035Sstevep * the most recent valid repository if possible when configd 14576035Sstevep * is restarted for some reasons or when system crashes 14586035Sstevep * during the switch operation. The repository databases 14596035Sstevep * referenced here are indicators of successful switch 14606035Sstevep * operations. 14616035Sstevep */ 14626035Sstevep static void 14636035Sstevep backend_switch_recovery(void) 14646035Sstevep { 14656035Sstevep const char *fast_db = FAST_REPOSITORY_DB; 14666035Sstevep char *errp; 14676035Sstevep struct stat s_buf; 14686035Sstevep struct sqlite *be_db; 14696035Sstevep 14706035Sstevep 14716035Sstevep /* 14726035Sstevep * A good transient db containing most recent data can 14736035Sstevep * exist if system or svc.configd crashes during the 14746035Sstevep * switch operation. If that is the case, check its 14756035Sstevep * integrity and use it. 14766035Sstevep */ 14776035Sstevep if (stat(fast_db, &s_buf) < 0) { 14786035Sstevep return; 14796035Sstevep } 14806035Sstevep 14816035Sstevep /* 14826035Sstevep * Do sanity check on the db 14836035Sstevep */ 14846035Sstevep be_db = sqlite_open(fast_db, 0600, &errp); 14856035Sstevep 14866035Sstevep if (be_db != NULL) { 14876035Sstevep if (backend_switch_check(be_db, &errp) == 0) 14886035Sstevep (void) backend_switch_copy(fast_db, REPOSITORY_DB, 1); 14896035Sstevep } 14906035Sstevep 14916035Sstevep (void) unlink(fast_db); 14926035Sstevep } 14936035Sstevep 14940Sstevel@tonic-gate /*ARGSUSED*/ 14950Sstevel@tonic-gate static int 14960Sstevel@tonic-gate backend_integrity_callback(void *private, int narg, char **vals, char **cols) 14970Sstevel@tonic-gate { 14980Sstevel@tonic-gate char **out = private; 14990Sstevel@tonic-gate char *old = *out; 15000Sstevel@tonic-gate char *new; 15010Sstevel@tonic-gate const char *info; 15020Sstevel@tonic-gate size_t len; 15030Sstevel@tonic-gate int x; 15040Sstevel@tonic-gate 15050Sstevel@tonic-gate for (x = 0; x < narg; x++) { 15060Sstevel@tonic-gate if ((info = vals[x]) != NULL && 15070Sstevel@tonic-gate strcmp(info, "ok") != 0) { 15080Sstevel@tonic-gate len = (old == NULL)? 0 : strlen(old); 15090Sstevel@tonic-gate len += strlen(info) + 2; /* '\n' + '\0' */ 15100Sstevel@tonic-gate 15110Sstevel@tonic-gate new = realloc(old, len); 15120Sstevel@tonic-gate if (new == NULL) 15130Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 15140Sstevel@tonic-gate if (old == NULL) 15150Sstevel@tonic-gate new[0] = 0; 15160Sstevel@tonic-gate old = *out = new; 15170Sstevel@tonic-gate (void) strlcat(new, info, len); 15180Sstevel@tonic-gate (void) strlcat(new, "\n", len); 15190Sstevel@tonic-gate } 15200Sstevel@tonic-gate } 15210Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 15220Sstevel@tonic-gate } 15230Sstevel@tonic-gate 15240Sstevel@tonic-gate #define BACKEND_CREATE_LOCKED -2 15250Sstevel@tonic-gate #define BACKEND_CREATE_FAIL -1 15260Sstevel@tonic-gate #define BACKEND_CREATE_SUCCESS 0 15270Sstevel@tonic-gate #define BACKEND_CREATE_READONLY 1 15280Sstevel@tonic-gate #define BACKEND_CREATE_NEED_INIT 2 15290Sstevel@tonic-gate static int 15300Sstevel@tonic-gate backend_create(backend_type_t backend_id, const char *db_file, 15310Sstevel@tonic-gate sqlite_backend_t **bep) 15320Sstevel@tonic-gate { 15330Sstevel@tonic-gate char *errp; 15340Sstevel@tonic-gate char *integrity_results = NULL; 15350Sstevel@tonic-gate sqlite_backend_t *be; 15360Sstevel@tonic-gate int r; 15370Sstevel@tonic-gate uint32_t val = -1UL; 15380Sstevel@tonic-gate struct run_single_int_info info; 15390Sstevel@tonic-gate int fd; 15400Sstevel@tonic-gate 15410Sstevel@tonic-gate assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); 15420Sstevel@tonic-gate 15430Sstevel@tonic-gate be = &be_info[backend_id]; 1544*7128Samaguire 15450Sstevel@tonic-gate assert(be->be_db == NULL); 15460Sstevel@tonic-gate 15470Sstevel@tonic-gate (void) pthread_mutex_init(&be->be_lock, NULL); 15480Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 15490Sstevel@tonic-gate 15500Sstevel@tonic-gate be->be_type = backend_id; 15510Sstevel@tonic-gate be->be_path = strdup(db_file); 15520Sstevel@tonic-gate if (be->be_path == NULL) { 15530Sstevel@tonic-gate perror("malloc"); 15540Sstevel@tonic-gate goto fail; 15550Sstevel@tonic-gate } 15560Sstevel@tonic-gate 15570Sstevel@tonic-gate be->be_db = sqlite_open(be->be_path, 0600, &errp); 15580Sstevel@tonic-gate 15590Sstevel@tonic-gate if (be->be_db == NULL) { 15600Sstevel@tonic-gate if (strstr(errp, "out of memory") != NULL) { 15610Sstevel@tonic-gate configd_critical("%s: %s\n", db_file, errp); 15620Sstevel@tonic-gate free(errp); 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate goto fail; 15650Sstevel@tonic-gate } 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate /* report it as an integrity failure */ 15680Sstevel@tonic-gate integrity_results = errp; 15690Sstevel@tonic-gate errp = NULL; 15700Sstevel@tonic-gate goto integrity_fail; 15710Sstevel@tonic-gate } 15720Sstevel@tonic-gate 15730Sstevel@tonic-gate /* 15740Sstevel@tonic-gate * check if we are inited and of the correct schema version 15750Sstevel@tonic-gate * 15760Sstevel@tonic-gate */ 15770Sstevel@tonic-gate info.rs_out = &val; 15780Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 15790Sstevel@tonic-gate 15800Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT schema_version FROM schema_version;", 15810Sstevel@tonic-gate run_single_int_callback, &info, &errp); 15820Sstevel@tonic-gate if (r == SQLITE_ERROR && 15830Sstevel@tonic-gate strcmp("no such table: schema_version", errp) == 0) { 15840Sstevel@tonic-gate free(errp); 15850Sstevel@tonic-gate /* 15860Sstevel@tonic-gate * Could be an empty repository, could be pre-schema_version 15870Sstevel@tonic-gate * schema. Check for id_tbl, which has always been there. 15880Sstevel@tonic-gate */ 15890Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT count() FROM id_tbl;", 15900Sstevel@tonic-gate NULL, NULL, &errp); 15910Sstevel@tonic-gate if (r == SQLITE_ERROR && 15920Sstevel@tonic-gate strcmp("no such table: id_tbl", errp) == 0) { 15930Sstevel@tonic-gate free(errp); 15940Sstevel@tonic-gate *bep = be; 15950Sstevel@tonic-gate return (BACKEND_CREATE_NEED_INIT); 15960Sstevel@tonic-gate } 15970Sstevel@tonic-gate 15980Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", db_file); 15990Sstevel@tonic-gate goto fail; 16000Sstevel@tonic-gate } 16010Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 16020Sstevel@tonic-gate free(errp); 16030Sstevel@tonic-gate *bep = NULL; 16040Sstevel@tonic-gate backend_destroy(be); 16050Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 16060Sstevel@tonic-gate } 16070Sstevel@tonic-gate if (r == SQLITE_OK) { 16080Sstevel@tonic-gate if (info.rs_result == REP_PROTOCOL_FAIL_NOT_FOUND || 16090Sstevel@tonic-gate val != BACKEND_SCHEMA_VERSION) { 16100Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", 16110Sstevel@tonic-gate db_file); 16120Sstevel@tonic-gate goto fail; 16130Sstevel@tonic-gate } 16140Sstevel@tonic-gate } 16150Sstevel@tonic-gate 16160Sstevel@tonic-gate /* 16170Sstevel@tonic-gate * pull in the whole database sequentially. 16180Sstevel@tonic-gate */ 16190Sstevel@tonic-gate if ((fd = open(db_file, O_RDONLY)) >= 0) { 16200Sstevel@tonic-gate size_t sz = 64 * 1024; 16210Sstevel@tonic-gate char *buffer = malloc(sz); 16220Sstevel@tonic-gate if (buffer != NULL) { 16230Sstevel@tonic-gate while (read(fd, buffer, sz) > 0) 16240Sstevel@tonic-gate ; 16250Sstevel@tonic-gate free(buffer); 16260Sstevel@tonic-gate } 16270Sstevel@tonic-gate (void) close(fd); 16280Sstevel@tonic-gate } 16290Sstevel@tonic-gate 16300Sstevel@tonic-gate /* 16310Sstevel@tonic-gate * run an integrity check 16320Sstevel@tonic-gate */ 16330Sstevel@tonic-gate r = sqlite_exec(be->be_db, "PRAGMA integrity_check;", 16340Sstevel@tonic-gate backend_integrity_callback, &integrity_results, &errp); 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 16370Sstevel@tonic-gate free(errp); 16380Sstevel@tonic-gate *bep = NULL; 16390Sstevel@tonic-gate backend_destroy(be); 16400Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 16410Sstevel@tonic-gate } 16420Sstevel@tonic-gate if (r == SQLITE_ABORT) { 16430Sstevel@tonic-gate free(errp); 16440Sstevel@tonic-gate errp = NULL; 16450Sstevel@tonic-gate integrity_results = "out of memory running integrity check\n"; 16460Sstevel@tonic-gate } else if (r != SQLITE_OK && integrity_results == NULL) { 16470Sstevel@tonic-gate integrity_results = errp; 16480Sstevel@tonic-gate errp = NULL; 16490Sstevel@tonic-gate } 16500Sstevel@tonic-gate 16510Sstevel@tonic-gate integrity_fail: 16520Sstevel@tonic-gate if (integrity_results != NULL) { 16530Sstevel@tonic-gate const char *fname = "/etc/svc/volatile/db_errors"; 16540Sstevel@tonic-gate if ((fd = open(fname, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { 16550Sstevel@tonic-gate fname = NULL; 16560Sstevel@tonic-gate } else { 16570Sstevel@tonic-gate if (backend_fd_write(fd, "\n\n") < 0 || 16580Sstevel@tonic-gate backend_fd_write(fd, db_file) < 0 || 16590Sstevel@tonic-gate backend_fd_write(fd, 16600Sstevel@tonic-gate ": PRAGMA integrity_check; failed. Results:\n") < 16610Sstevel@tonic-gate 0 || backend_fd_write(fd, integrity_results) < 0 || 16620Sstevel@tonic-gate backend_fd_write(fd, "\n\n") < 0) { 16630Sstevel@tonic-gate fname = NULL; 16640Sstevel@tonic-gate } 16650Sstevel@tonic-gate (void) close(fd); 16660Sstevel@tonic-gate } 16670Sstevel@tonic-gate 16680Sstevel@tonic-gate if (!is_main_repository || 16690Sstevel@tonic-gate backend_id == BACKEND_TYPE_NONPERSIST) { 16700Sstevel@tonic-gate if (fname != NULL) 16710Sstevel@tonic-gate configd_critical( 16720Sstevel@tonic-gate "%s: integrity check failed. Details in " 16730Sstevel@tonic-gate "%s\n", db_file, fname); 16740Sstevel@tonic-gate else 16750Sstevel@tonic-gate configd_critical( 16764931Spjung "%s: integrity check failed.\n", 16770Sstevel@tonic-gate db_file); 16780Sstevel@tonic-gate } else { 16790Sstevel@tonic-gate (void) fprintf(stderr, 16800Sstevel@tonic-gate "\n" 16810Sstevel@tonic-gate "svc.configd: smf(5) database integrity check of:\n" 16820Sstevel@tonic-gate "\n" 16830Sstevel@tonic-gate " %s\n" 16840Sstevel@tonic-gate "\n" 16850Sstevel@tonic-gate " failed. The database might be damaged or a media error might have\n" 16860Sstevel@tonic-gate " prevented it from being verified. Additional information useful to\n" 16870Sstevel@tonic-gate " your service provider%s%s\n" 16880Sstevel@tonic-gate "\n" 16890Sstevel@tonic-gate " The system will not be able to boot until you have restored a working\n" 16900Sstevel@tonic-gate " database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n" 16910Sstevel@tonic-gate " purposes. The command:\n" 16920Sstevel@tonic-gate "\n" 16930Sstevel@tonic-gate " /lib/svc/bin/restore_repository\n" 16940Sstevel@tonic-gate "\n" 16950Sstevel@tonic-gate " can be run to restore a backup version of your repository. See\n" 16960Sstevel@tonic-gate " http://sun.com/msg/SMF-8000-MY for more information.\n" 16970Sstevel@tonic-gate "\n", 16984520Snw141292 db_file, 16994520Snw141292 (fname == NULL)? ":\n\n" : " is in:\n\n ", 17004520Snw141292 (fname == NULL)? integrity_results : fname); 17010Sstevel@tonic-gate } 17020Sstevel@tonic-gate free(errp); 17030Sstevel@tonic-gate goto fail; 17040Sstevel@tonic-gate } 17050Sstevel@tonic-gate 17060Sstevel@tonic-gate /* 1707*7128Samaguire * Simply do check if backend has been upgraded. We do not wish 1708*7128Samaguire * to actually carry out upgrade here - the main repository may 1709*7128Samaguire * not be writable at this point. Actual upgrade is carried out 1710*7128Samaguire * via backend_check_readonly(). This check is done so that 1711*7128Samaguire * we determine repository state - upgraded or not - and then 1712*7128Samaguire * the appropriate SELECT statement (value-ordered or not) 1713*7128Samaguire * can be used when retrieving property values early in boot. 1714*7128Samaguire */ 1715*7128Samaguire if (backend_id == BACKEND_TYPE_NORMAL) 1716*7128Samaguire backend_check_upgrade(be, B_FALSE); 1717*7128Samaguire /* 17180Sstevel@tonic-gate * check if we are writable 17190Sstevel@tonic-gate */ 1720407Sjwadams r = backend_is_readonly(be->be_db, be->be_path); 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 17230Sstevel@tonic-gate free(errp); 17240Sstevel@tonic-gate *bep = NULL; 17250Sstevel@tonic-gate backend_destroy(be); 17260Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 17270Sstevel@tonic-gate } 17280Sstevel@tonic-gate if (r != SQLITE_OK && r != SQLITE_FULL) { 17290Sstevel@tonic-gate free(errp); 17300Sstevel@tonic-gate be->be_readonly = 1; 17310Sstevel@tonic-gate *bep = be; 17320Sstevel@tonic-gate return (BACKEND_CREATE_READONLY); 17330Sstevel@tonic-gate } 1734*7128Samaguire 17350Sstevel@tonic-gate *bep = be; 17360Sstevel@tonic-gate return (BACKEND_CREATE_SUCCESS); 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate fail: 17390Sstevel@tonic-gate *bep = NULL; 17400Sstevel@tonic-gate backend_destroy(be); 17410Sstevel@tonic-gate return (BACKEND_CREATE_FAIL); 17420Sstevel@tonic-gate } 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate /* 17450Sstevel@tonic-gate * (arg & -arg) is, through the magic of twos-complement arithmetic, the 17460Sstevel@tonic-gate * lowest set bit in arg. 17470Sstevel@tonic-gate */ 17480Sstevel@tonic-gate static size_t 17490Sstevel@tonic-gate round_up_to_p2(size_t arg) 17500Sstevel@tonic-gate { 17510Sstevel@tonic-gate /* 17520Sstevel@tonic-gate * Don't allow a zero result. 17530Sstevel@tonic-gate */ 17540Sstevel@tonic-gate assert(arg > 0 && ((ssize_t)arg > 0)); 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate while ((arg & (arg - 1)) != 0) 17570Sstevel@tonic-gate arg += (arg & -arg); 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate return (arg); 17600Sstevel@tonic-gate } 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate /* 17630Sstevel@tonic-gate * Returns 17640Sstevel@tonic-gate * _NO_RESOURCES - out of memory 17650Sstevel@tonic-gate * _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist 17660Sstevel@tonic-gate * _DONE - callback aborted query 17670Sstevel@tonic-gate * _SUCCESS 17680Sstevel@tonic-gate */ 17690Sstevel@tonic-gate int 17700Sstevel@tonic-gate backend_run(backend_type_t t, backend_query_t *q, 17710Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 17720Sstevel@tonic-gate { 17730Sstevel@tonic-gate char *errmsg = NULL; 17740Sstevel@tonic-gate int ret; 17750Sstevel@tonic-gate sqlite_backend_t *be; 17760Sstevel@tonic-gate hrtime_t ts, vts; 17770Sstevel@tonic-gate 17780Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 17790Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 17800Sstevel@tonic-gate 17810Sstevel@tonic-gate if ((ret = backend_lock(t, 0, &be)) != REP_PROTOCOL_SUCCESS) 17820Sstevel@tonic-gate return (ret); 17830Sstevel@tonic-gate 17840Sstevel@tonic-gate ts = gethrtime(); 17850Sstevel@tonic-gate vts = gethrvtime(); 17860Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 17870Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 17880Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 17890Sstevel@tonic-gate backend_unlock(be); 17900Sstevel@tonic-gate 17910Sstevel@tonic-gate return (ret); 17920Sstevel@tonic-gate } 17930Sstevel@tonic-gate 17940Sstevel@tonic-gate /* 17950Sstevel@tonic-gate * Starts a "read-only" transaction -- i.e., locks out writers as long 17960Sstevel@tonic-gate * as it is active. 17970Sstevel@tonic-gate * 17980Sstevel@tonic-gate * Fails with 17990Sstevel@tonic-gate * _NO_RESOURCES - out of memory 18000Sstevel@tonic-gate * 18010Sstevel@tonic-gate * If t is not _NORMAL, can also fail with 18020Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 18030Sstevel@tonic-gate * 18040Sstevel@tonic-gate * If writable is true, can also fail with 18050Sstevel@tonic-gate * _BACKEND_READONLY 18060Sstevel@tonic-gate */ 18070Sstevel@tonic-gate static int 18080Sstevel@tonic-gate backend_tx_begin_common(backend_type_t t, backend_tx_t **txp, int writable) 18090Sstevel@tonic-gate { 18100Sstevel@tonic-gate backend_tx_t *ret; 18110Sstevel@tonic-gate sqlite_backend_t *be; 18120Sstevel@tonic-gate int r; 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate *txp = NULL; 18150Sstevel@tonic-gate 18160Sstevel@tonic-gate ret = uu_zalloc(sizeof (*ret)); 18170Sstevel@tonic-gate if (ret == NULL) 18180Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate if ((r = backend_lock(t, writable, &be)) != REP_PROTOCOL_SUCCESS) { 18210Sstevel@tonic-gate uu_free(ret); 18220Sstevel@tonic-gate return (r); 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate 18250Sstevel@tonic-gate ret->bt_be = be; 18260Sstevel@tonic-gate ret->bt_readonly = !writable; 18270Sstevel@tonic-gate ret->bt_type = t; 18280Sstevel@tonic-gate ret->bt_full = 0; 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate *txp = ret; 18310Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 18320Sstevel@tonic-gate } 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate int 18350Sstevel@tonic-gate backend_tx_begin_ro(backend_type_t t, backend_tx_t **txp) 18360Sstevel@tonic-gate { 18370Sstevel@tonic-gate return (backend_tx_begin_common(t, txp, 0)); 18380Sstevel@tonic-gate } 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate static void 18410Sstevel@tonic-gate backend_tx_end(backend_tx_t *tx) 18420Sstevel@tonic-gate { 18430Sstevel@tonic-gate sqlite_backend_t *be; 18440Sstevel@tonic-gate 18450Sstevel@tonic-gate be = tx->bt_be; 18460Sstevel@tonic-gate 18470Sstevel@tonic-gate if (tx->bt_full) { 18480Sstevel@tonic-gate struct sqlite *new; 18490Sstevel@tonic-gate 18500Sstevel@tonic-gate /* 18510Sstevel@tonic-gate * sqlite tends to be sticky with SQLITE_FULL, so we try 18520Sstevel@tonic-gate * to get a fresh database handle if we got a FULL warning 18530Sstevel@tonic-gate * along the way. If that fails, no harm done. 18540Sstevel@tonic-gate */ 18550Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, NULL); 18560Sstevel@tonic-gate if (new != NULL) { 18570Sstevel@tonic-gate sqlite_close(be->be_db); 18580Sstevel@tonic-gate be->be_db = new; 18590Sstevel@tonic-gate } 18600Sstevel@tonic-gate } 18610Sstevel@tonic-gate backend_unlock(be); 18620Sstevel@tonic-gate tx->bt_be = NULL; 18630Sstevel@tonic-gate uu_free(tx); 18640Sstevel@tonic-gate } 18650Sstevel@tonic-gate 18660Sstevel@tonic-gate void 18670Sstevel@tonic-gate backend_tx_end_ro(backend_tx_t *tx) 18680Sstevel@tonic-gate { 18690Sstevel@tonic-gate assert(tx->bt_readonly); 18700Sstevel@tonic-gate backend_tx_end(tx); 18710Sstevel@tonic-gate } 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate /* 18740Sstevel@tonic-gate * Fails with 18750Sstevel@tonic-gate * _NO_RESOURCES - out of memory 18760Sstevel@tonic-gate * _BACKEND_ACCESS 18770Sstevel@tonic-gate * _BACKEND_READONLY 18780Sstevel@tonic-gate */ 18790Sstevel@tonic-gate int 18800Sstevel@tonic-gate backend_tx_begin(backend_type_t t, backend_tx_t **txp) 18810Sstevel@tonic-gate { 18820Sstevel@tonic-gate int r; 18830Sstevel@tonic-gate char *errmsg; 18840Sstevel@tonic-gate hrtime_t ts, vts; 18850Sstevel@tonic-gate 18860Sstevel@tonic-gate r = backend_tx_begin_common(t, txp, 1); 18870Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 18880Sstevel@tonic-gate return (r); 18890Sstevel@tonic-gate 18900Sstevel@tonic-gate ts = gethrtime(); 18910Sstevel@tonic-gate vts = gethrvtime(); 18920Sstevel@tonic-gate r = sqlite_exec((*txp)->bt_be->be_db, "BEGIN TRANSACTION", NULL, NULL, 18930Sstevel@tonic-gate &errmsg); 18940Sstevel@tonic-gate UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); 18950Sstevel@tonic-gate if (r == SQLITE_FULL) 18960Sstevel@tonic-gate (*txp)->bt_full = 1; 18970Sstevel@tonic-gate r = backend_error((*txp)->bt_be, r, errmsg); 18980Sstevel@tonic-gate 18990Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 19000Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 19010Sstevel@tonic-gate (void) sqlite_exec((*txp)->bt_be->be_db, 19020Sstevel@tonic-gate "ROLLBACK TRANSACTION", NULL, NULL, NULL); 19030Sstevel@tonic-gate backend_tx_end(*txp); 19040Sstevel@tonic-gate *txp = NULL; 19050Sstevel@tonic-gate return (r); 19060Sstevel@tonic-gate } 19070Sstevel@tonic-gate 19080Sstevel@tonic-gate (*txp)->bt_readonly = 0; 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 19110Sstevel@tonic-gate } 19120Sstevel@tonic-gate 19130Sstevel@tonic-gate void 19140Sstevel@tonic-gate backend_tx_rollback(backend_tx_t *tx) 19150Sstevel@tonic-gate { 19160Sstevel@tonic-gate int r; 19170Sstevel@tonic-gate char *errmsg; 19180Sstevel@tonic-gate sqlite_backend_t *be; 19190Sstevel@tonic-gate hrtime_t ts, vts; 19200Sstevel@tonic-gate 19210Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 19220Sstevel@tonic-gate be = tx->bt_be; 19230Sstevel@tonic-gate 19240Sstevel@tonic-gate ts = gethrtime(); 19250Sstevel@tonic-gate vts = gethrvtime(); 19260Sstevel@tonic-gate r = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 19270Sstevel@tonic-gate &errmsg); 19280Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 19290Sstevel@tonic-gate if (r == SQLITE_FULL) 19300Sstevel@tonic-gate tx->bt_full = 1; 19310Sstevel@tonic-gate (void) backend_error(be, r, errmsg); 19320Sstevel@tonic-gate 19330Sstevel@tonic-gate backend_tx_end(tx); 19340Sstevel@tonic-gate } 19350Sstevel@tonic-gate 19360Sstevel@tonic-gate /* 19370Sstevel@tonic-gate * Fails with 19380Sstevel@tonic-gate * _NO_RESOURCES - out of memory 19390Sstevel@tonic-gate */ 19400Sstevel@tonic-gate int 19410Sstevel@tonic-gate backend_tx_commit(backend_tx_t *tx) 19420Sstevel@tonic-gate { 19430Sstevel@tonic-gate int r, r2; 19440Sstevel@tonic-gate char *errmsg; 19450Sstevel@tonic-gate sqlite_backend_t *be; 19460Sstevel@tonic-gate hrtime_t ts, vts; 19470Sstevel@tonic-gate 19480Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 19490Sstevel@tonic-gate be = tx->bt_be; 19500Sstevel@tonic-gate ts = gethrtime(); 19510Sstevel@tonic-gate vts = gethrvtime(); 19520Sstevel@tonic-gate r = sqlite_exec(be->be_db, "COMMIT TRANSACTION", NULL, NULL, 19530Sstevel@tonic-gate &errmsg); 19540Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 19550Sstevel@tonic-gate if (r == SQLITE_FULL) 19560Sstevel@tonic-gate tx->bt_full = 1; 19570Sstevel@tonic-gate 19580Sstevel@tonic-gate r = backend_error(be, r, errmsg); 19590Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 19600Sstevel@tonic-gate 19610Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 19620Sstevel@tonic-gate r2 = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 19630Sstevel@tonic-gate &errmsg); 19640Sstevel@tonic-gate r2 = backend_error(be, r2, errmsg); 19650Sstevel@tonic-gate if (r2 != REP_PROTOCOL_SUCCESS) 19660Sstevel@tonic-gate backend_panic("cannot rollback failed commit"); 19670Sstevel@tonic-gate 19680Sstevel@tonic-gate backend_tx_end(tx); 19690Sstevel@tonic-gate return (r); 19700Sstevel@tonic-gate } 19710Sstevel@tonic-gate backend_tx_end(tx); 19720Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 19730Sstevel@tonic-gate } 19740Sstevel@tonic-gate 19750Sstevel@tonic-gate static const char * 19760Sstevel@tonic-gate id_space_to_name(enum id_space id) 19770Sstevel@tonic-gate { 19780Sstevel@tonic-gate switch (id) { 19790Sstevel@tonic-gate case BACKEND_ID_SERVICE_INSTANCE: 19800Sstevel@tonic-gate return ("SI"); 19810Sstevel@tonic-gate case BACKEND_ID_PROPERTYGRP: 19820Sstevel@tonic-gate return ("PG"); 19830Sstevel@tonic-gate case BACKEND_ID_GENERATION: 19840Sstevel@tonic-gate return ("GEN"); 19850Sstevel@tonic-gate case BACKEND_ID_PROPERTY: 19860Sstevel@tonic-gate return ("PROP"); 19870Sstevel@tonic-gate case BACKEND_ID_VALUE: 19880Sstevel@tonic-gate return ("VAL"); 19890Sstevel@tonic-gate case BACKEND_ID_SNAPNAME: 19900Sstevel@tonic-gate return ("SNAME"); 19910Sstevel@tonic-gate case BACKEND_ID_SNAPSHOT: 19920Sstevel@tonic-gate return ("SHOT"); 19930Sstevel@tonic-gate case BACKEND_ID_SNAPLEVEL: 19940Sstevel@tonic-gate return ("SLVL"); 19950Sstevel@tonic-gate default: 19960Sstevel@tonic-gate abort(); 19970Sstevel@tonic-gate /*NOTREACHED*/ 19980Sstevel@tonic-gate } 19990Sstevel@tonic-gate } 20000Sstevel@tonic-gate 20010Sstevel@tonic-gate /* 20020Sstevel@tonic-gate * Returns a new id or 0 if the id argument is invalid or the query fails. 20030Sstevel@tonic-gate */ 20040Sstevel@tonic-gate uint32_t 20050Sstevel@tonic-gate backend_new_id(backend_tx_t *tx, enum id_space id) 20060Sstevel@tonic-gate { 20070Sstevel@tonic-gate struct run_single_int_info info; 20080Sstevel@tonic-gate uint32_t new_id = 0; 20090Sstevel@tonic-gate const char *name = id_space_to_name(id); 20100Sstevel@tonic-gate char *errmsg; 20110Sstevel@tonic-gate int ret; 20120Sstevel@tonic-gate sqlite_backend_t *be; 20130Sstevel@tonic-gate hrtime_t ts, vts; 20140Sstevel@tonic-gate 20150Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 20160Sstevel@tonic-gate be = tx->bt_be; 20170Sstevel@tonic-gate 20180Sstevel@tonic-gate info.rs_out = &new_id; 20190Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 20200Sstevel@tonic-gate 20210Sstevel@tonic-gate ts = gethrtime(); 20220Sstevel@tonic-gate vts = gethrvtime(); 20230Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 20240Sstevel@tonic-gate "SELECT id_next FROM id_tbl WHERE (id_name = '%q');" 20250Sstevel@tonic-gate "UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');", 20260Sstevel@tonic-gate run_single_int_callback, &info, &errmsg, name, name); 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 20310Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 20320Sstevel@tonic-gate 20330Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) { 20340Sstevel@tonic-gate return (0); 20350Sstevel@tonic-gate } 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate return (new_id); 20380Sstevel@tonic-gate } 20390Sstevel@tonic-gate 20400Sstevel@tonic-gate /* 20410Sstevel@tonic-gate * Returns 20420Sstevel@tonic-gate * _NO_RESOURCES - out of memory 20430Sstevel@tonic-gate * _DONE - callback aborted query 20440Sstevel@tonic-gate * _SUCCESS 20450Sstevel@tonic-gate */ 20460Sstevel@tonic-gate int 20470Sstevel@tonic-gate backend_tx_run(backend_tx_t *tx, backend_query_t *q, 20480Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 20490Sstevel@tonic-gate { 20500Sstevel@tonic-gate char *errmsg = NULL; 20510Sstevel@tonic-gate int ret; 20520Sstevel@tonic-gate sqlite_backend_t *be; 20530Sstevel@tonic-gate hrtime_t ts, vts; 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL); 20560Sstevel@tonic-gate be = tx->bt_be; 20570Sstevel@tonic-gate 20580Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 20590Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 20600Sstevel@tonic-gate 20610Sstevel@tonic-gate ts = gethrtime(); 20620Sstevel@tonic-gate vts = gethrvtime(); 20630Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 20640Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 20650Sstevel@tonic-gate if (ret == SQLITE_FULL) 20660Sstevel@tonic-gate tx->bt_full = 1; 20670Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 20680Sstevel@tonic-gate 20690Sstevel@tonic-gate return (ret); 20700Sstevel@tonic-gate } 20710Sstevel@tonic-gate 20720Sstevel@tonic-gate /* 20730Sstevel@tonic-gate * Returns 20740Sstevel@tonic-gate * _NO_RESOURCES - out of memory 20750Sstevel@tonic-gate * _NOT_FOUND - the query returned no results 20760Sstevel@tonic-gate * _SUCCESS - the query returned a single integer 20770Sstevel@tonic-gate */ 20780Sstevel@tonic-gate int 20790Sstevel@tonic-gate backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q, uint32_t *buf) 20800Sstevel@tonic-gate { 20810Sstevel@tonic-gate struct run_single_int_info info; 20820Sstevel@tonic-gate int ret; 20830Sstevel@tonic-gate 20840Sstevel@tonic-gate info.rs_out = buf; 20850Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 20860Sstevel@tonic-gate 20870Sstevel@tonic-gate ret = backend_tx_run(tx, q, run_single_int_callback, &info); 20880Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 20890Sstevel@tonic-gate 20900Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 20910Sstevel@tonic-gate return (ret); 20920Sstevel@tonic-gate 20930Sstevel@tonic-gate return (info.rs_result); 20940Sstevel@tonic-gate } 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate /* 20970Sstevel@tonic-gate * Fails with 20980Sstevel@tonic-gate * _NO_RESOURCES - out of memory 20990Sstevel@tonic-gate */ 21000Sstevel@tonic-gate int 21010Sstevel@tonic-gate backend_tx_run_update(backend_tx_t *tx, const char *format, ...) 21020Sstevel@tonic-gate { 21030Sstevel@tonic-gate va_list a; 21040Sstevel@tonic-gate char *errmsg; 21050Sstevel@tonic-gate int ret; 21060Sstevel@tonic-gate sqlite_backend_t *be; 21070Sstevel@tonic-gate hrtime_t ts, vts; 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 21100Sstevel@tonic-gate be = tx->bt_be; 21110Sstevel@tonic-gate 21120Sstevel@tonic-gate va_start(a, format); 21130Sstevel@tonic-gate ts = gethrtime(); 21140Sstevel@tonic-gate vts = gethrvtime(); 21150Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 21160Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 21170Sstevel@tonic-gate if (ret == SQLITE_FULL) 21180Sstevel@tonic-gate tx->bt_full = 1; 21190Sstevel@tonic-gate va_end(a); 21200Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 21210Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 21220Sstevel@tonic-gate 21230Sstevel@tonic-gate return (ret); 21240Sstevel@tonic-gate } 21250Sstevel@tonic-gate 21260Sstevel@tonic-gate /* 21270Sstevel@tonic-gate * returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured 21280Sstevel@tonic-gate */ 21290Sstevel@tonic-gate int 21300Sstevel@tonic-gate backend_tx_run_update_changed(backend_tx_t *tx, const char *format, ...) 21310Sstevel@tonic-gate { 21320Sstevel@tonic-gate va_list a; 21330Sstevel@tonic-gate char *errmsg; 21340Sstevel@tonic-gate int ret; 21350Sstevel@tonic-gate sqlite_backend_t *be; 21360Sstevel@tonic-gate hrtime_t ts, vts; 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 21390Sstevel@tonic-gate be = tx->bt_be; 21400Sstevel@tonic-gate 21410Sstevel@tonic-gate va_start(a, format); 21420Sstevel@tonic-gate ts = gethrtime(); 21430Sstevel@tonic-gate vts = gethrvtime(); 21440Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 21450Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 21460Sstevel@tonic-gate if (ret == SQLITE_FULL) 21470Sstevel@tonic-gate tx->bt_full = 1; 21480Sstevel@tonic-gate va_end(a); 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 21510Sstevel@tonic-gate 21520Sstevel@tonic-gate return (ret); 21530Sstevel@tonic-gate } 21540Sstevel@tonic-gate 21550Sstevel@tonic-gate #define BACKEND_ADD_SCHEMA(be, file, tbls, idxs) \ 21560Sstevel@tonic-gate (backend_add_schema((be), (file), \ 21570Sstevel@tonic-gate (tbls), sizeof (tbls) / sizeof (*(tbls)), \ 21580Sstevel@tonic-gate (idxs), sizeof (idxs) / sizeof (*(idxs)))) 21590Sstevel@tonic-gate 21600Sstevel@tonic-gate static int 21610Sstevel@tonic-gate backend_add_schema(sqlite_backend_t *be, const char *file, 21620Sstevel@tonic-gate struct backend_tbl_info *tbls, int tbl_count, 21630Sstevel@tonic-gate struct backend_idx_info *idxs, int idx_count) 21640Sstevel@tonic-gate { 21650Sstevel@tonic-gate int i; 21660Sstevel@tonic-gate char *errmsg; 21670Sstevel@tonic-gate int ret; 21680Sstevel@tonic-gate 21690Sstevel@tonic-gate /* 21700Sstevel@tonic-gate * Create the tables. 21710Sstevel@tonic-gate */ 21720Sstevel@tonic-gate for (i = 0; i < tbl_count; i++) { 21730Sstevel@tonic-gate if (tbls[i].bti_name == NULL) { 21740Sstevel@tonic-gate assert(i + 1 == tbl_count); 21750Sstevel@tonic-gate break; 21760Sstevel@tonic-gate } 21770Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 21780Sstevel@tonic-gate "CREATE TABLE %s (%s);\n", 21790Sstevel@tonic-gate NULL, NULL, &errmsg, tbls[i].bti_name, tbls[i].bti_cols); 21800Sstevel@tonic-gate 21810Sstevel@tonic-gate if (ret != SQLITE_OK) { 21820Sstevel@tonic-gate configd_critical( 21830Sstevel@tonic-gate "%s: %s table creation fails: %s\n", file, 21840Sstevel@tonic-gate tbls[i].bti_name, errmsg); 21850Sstevel@tonic-gate free(errmsg); 21860Sstevel@tonic-gate return (-1); 21870Sstevel@tonic-gate } 21880Sstevel@tonic-gate } 21890Sstevel@tonic-gate 21900Sstevel@tonic-gate /* 21910Sstevel@tonic-gate * Make indices on key tables and columns. 21920Sstevel@tonic-gate */ 21930Sstevel@tonic-gate for (i = 0; i < idx_count; i++) { 21940Sstevel@tonic-gate if (idxs[i].bxi_tbl == NULL) { 21950Sstevel@tonic-gate assert(i + 1 == idx_count); 21960Sstevel@tonic-gate break; 21970Sstevel@tonic-gate } 21980Sstevel@tonic-gate 21990Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 22000Sstevel@tonic-gate "CREATE INDEX %s_%s ON %s (%s);\n", 22010Sstevel@tonic-gate NULL, NULL, &errmsg, idxs[i].bxi_tbl, idxs[i].bxi_idx, 22020Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_cols); 22030Sstevel@tonic-gate 22040Sstevel@tonic-gate if (ret != SQLITE_OK) { 22050Sstevel@tonic-gate configd_critical( 22060Sstevel@tonic-gate "%s: %s_%s index creation fails: %s\n", file, 22070Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_idx, errmsg); 22080Sstevel@tonic-gate free(errmsg); 22090Sstevel@tonic-gate return (-1); 22100Sstevel@tonic-gate } 22110Sstevel@tonic-gate } 22120Sstevel@tonic-gate return (0); 22130Sstevel@tonic-gate } 22140Sstevel@tonic-gate 22150Sstevel@tonic-gate static int 22160Sstevel@tonic-gate backend_init_schema(sqlite_backend_t *be, const char *db_file, backend_type_t t) 22170Sstevel@tonic-gate { 22180Sstevel@tonic-gate int i; 22190Sstevel@tonic-gate char *errmsg; 22200Sstevel@tonic-gate int ret; 22210Sstevel@tonic-gate 22220Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || t == BACKEND_TYPE_NONPERSIST); 22230Sstevel@tonic-gate 22240Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) { 22250Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_normal, idxs_normal); 22260Sstevel@tonic-gate } else if (t == BACKEND_TYPE_NONPERSIST) { 22270Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_np, idxs_np); 22280Sstevel@tonic-gate } else { 22290Sstevel@tonic-gate abort(); /* can't happen */ 22300Sstevel@tonic-gate } 22310Sstevel@tonic-gate 22320Sstevel@tonic-gate if (ret < 0) { 22330Sstevel@tonic-gate return (ret); 22340Sstevel@tonic-gate } 22350Sstevel@tonic-gate 22360Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_common, idxs_common); 22370Sstevel@tonic-gate if (ret < 0) { 22380Sstevel@tonic-gate return (ret); 22390Sstevel@tonic-gate } 22400Sstevel@tonic-gate 22410Sstevel@tonic-gate /* 22420Sstevel@tonic-gate * Add the schema version to the table 22430Sstevel@tonic-gate */ 22440Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 22450Sstevel@tonic-gate "INSERT INTO schema_version (schema_version) VALUES (%d)", 22460Sstevel@tonic-gate NULL, NULL, &errmsg, BACKEND_SCHEMA_VERSION); 22470Sstevel@tonic-gate if (ret != SQLITE_OK) { 22480Sstevel@tonic-gate configd_critical( 22490Sstevel@tonic-gate "setting schema version fails: %s\n", errmsg); 22500Sstevel@tonic-gate free(errmsg); 22510Sstevel@tonic-gate } 22520Sstevel@tonic-gate 22530Sstevel@tonic-gate /* 22540Sstevel@tonic-gate * Populate id_tbl with initial IDs. 22550Sstevel@tonic-gate */ 22560Sstevel@tonic-gate for (i = 0; i < BACKEND_ID_INVALID; i++) { 22570Sstevel@tonic-gate const char *name = id_space_to_name(i); 22580Sstevel@tonic-gate 22590Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 22600Sstevel@tonic-gate "INSERT INTO id_tbl (id_name, id_next) " 22610Sstevel@tonic-gate "VALUES ('%q', %d);", NULL, NULL, &errmsg, name, 1); 22620Sstevel@tonic-gate if (ret != SQLITE_OK) { 22630Sstevel@tonic-gate configd_critical( 22640Sstevel@tonic-gate "id insertion for %s fails: %s\n", name, errmsg); 22650Sstevel@tonic-gate free(errmsg); 22660Sstevel@tonic-gate return (-1); 22670Sstevel@tonic-gate } 22680Sstevel@tonic-gate } 22690Sstevel@tonic-gate /* 22700Sstevel@tonic-gate * Set the persistance of the database. The normal database is marked 22710Sstevel@tonic-gate * "synchronous", so that all writes are synchronized to stable storage 22720Sstevel@tonic-gate * before proceeding. 22730Sstevel@tonic-gate */ 22740Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 22750Sstevel@tonic-gate "PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;", 22760Sstevel@tonic-gate NULL, NULL, &errmsg, 22770Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF", 22780Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF"); 22790Sstevel@tonic-gate if (ret != SQLITE_OK) { 22800Sstevel@tonic-gate configd_critical("pragma setting fails: %s\n", errmsg); 22810Sstevel@tonic-gate free(errmsg); 22820Sstevel@tonic-gate return (-1); 22830Sstevel@tonic-gate } 22840Sstevel@tonic-gate 22850Sstevel@tonic-gate return (0); 22860Sstevel@tonic-gate } 22870Sstevel@tonic-gate 22880Sstevel@tonic-gate int 22890Sstevel@tonic-gate backend_init(const char *db_file, const char *npdb_file, int have_np) 22900Sstevel@tonic-gate { 22910Sstevel@tonic-gate sqlite_backend_t *be; 22920Sstevel@tonic-gate int r; 22930Sstevel@tonic-gate int writable_persist = 1; 22940Sstevel@tonic-gate 22950Sstevel@tonic-gate /* set up our temporary directory */ 22960Sstevel@tonic-gate sqlite_temp_directory = "/etc/svc/volatile"; 22970Sstevel@tonic-gate 22980Sstevel@tonic-gate if (strcmp(SQLITE_VERSION, sqlite_version) != 0) { 22990Sstevel@tonic-gate configd_critical("Mismatched link! (%s should be %s)\n", 23000Sstevel@tonic-gate sqlite_version, SQLITE_VERSION); 23010Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23020Sstevel@tonic-gate } 23036035Sstevep 23046035Sstevep /* 23056035Sstevep * If the system crashed during a backend switch, there might 23066035Sstevep * be a leftover transient database which contains useful 23076035Sstevep * information which can be used for recovery. 23086035Sstevep */ 23096035Sstevep backend_switch_recovery(); 23106035Sstevep 23110Sstevel@tonic-gate if (db_file == NULL) 23120Sstevel@tonic-gate db_file = REPOSITORY_DB; 23135777Stw21770 if (strcmp(db_file, REPOSITORY_DB) != 0) { 23145777Stw21770 is_main_repository = 0; 23155777Stw21770 } 23160Sstevel@tonic-gate 23170Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be); 23180Sstevel@tonic-gate switch (r) { 23190Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 23200Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23210Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 23220Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 23230Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 23240Sstevel@tonic-gate break; /* success */ 23250Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 23260Sstevel@tonic-gate writable_persist = 0; 23270Sstevel@tonic-gate break; 23280Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 23290Sstevel@tonic-gate if (backend_init_schema(be, db_file, BACKEND_TYPE_NORMAL)) { 23300Sstevel@tonic-gate backend_destroy(be); 23310Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23320Sstevel@tonic-gate } 23330Sstevel@tonic-gate break; 23340Sstevel@tonic-gate default: 23350Sstevel@tonic-gate abort(); 23360Sstevel@tonic-gate /*NOTREACHED*/ 23370Sstevel@tonic-gate } 23380Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NORMAL, be); 23390Sstevel@tonic-gate 23400Sstevel@tonic-gate if (have_np) { 23410Sstevel@tonic-gate if (npdb_file == NULL) 23420Sstevel@tonic-gate npdb_file = NONPERSIST_DB; 23430Sstevel@tonic-gate 23440Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NONPERSIST, npdb_file, &be); 23450Sstevel@tonic-gate switch (r) { 23460Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 23470Sstevel@tonic-gate break; /* success */ 23480Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 23490Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23500Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 23510Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 23520Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 23530Sstevel@tonic-gate configd_critical("%s: unable to write\n", npdb_file); 23540Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23550Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 23560Sstevel@tonic-gate if (backend_init_schema(be, db_file, 23570Sstevel@tonic-gate BACKEND_TYPE_NONPERSIST)) { 23580Sstevel@tonic-gate backend_destroy(be); 23590Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 23600Sstevel@tonic-gate } 23610Sstevel@tonic-gate break; 23620Sstevel@tonic-gate default: 23630Sstevel@tonic-gate abort(); 23640Sstevel@tonic-gate /*NOTREACHED*/ 23650Sstevel@tonic-gate } 23660Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NONPERSIST, be); 23670Sstevel@tonic-gate 23680Sstevel@tonic-gate /* 23690Sstevel@tonic-gate * If we started up with a writable filesystem, but the 23700Sstevel@tonic-gate * non-persistent database needed initialization, we 2371*7128Samaguire * are booting a non-global zone, so do a backup, and carry 2372*7128Samaguire * out upgrade if necessary. 23730Sstevel@tonic-gate */ 23740Sstevel@tonic-gate if (r == BACKEND_CREATE_NEED_INIT && writable_persist && 23750Sstevel@tonic-gate backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 23760Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 23770Sstevel@tonic-gate if (backend_create_backup_locked(be, 23780Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { 23790Sstevel@tonic-gate configd_critical( 23800Sstevel@tonic-gate "unable to create \"%s\" backup of " 23810Sstevel@tonic-gate "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 23820Sstevel@tonic-gate be->be_path); 23830Sstevel@tonic-gate } 2384*7128Samaguire backend_check_upgrade(be, B_TRUE); 23850Sstevel@tonic-gate backend_unlock(be); 23860Sstevel@tonic-gate } 23870Sstevel@tonic-gate } 23880Sstevel@tonic-gate return (CONFIGD_EXIT_OKAY); 23890Sstevel@tonic-gate } 23900Sstevel@tonic-gate 23910Sstevel@tonic-gate /* 23920Sstevel@tonic-gate * quiesce all database activity prior to exiting 23930Sstevel@tonic-gate */ 23940Sstevel@tonic-gate void 23950Sstevel@tonic-gate backend_fini(void) 23960Sstevel@tonic-gate { 23970Sstevel@tonic-gate sqlite_backend_t *be_normal, *be_np; 23980Sstevel@tonic-gate 23990Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NORMAL, 1, &be_normal); 24000Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NONPERSIST, 1, &be_np); 24010Sstevel@tonic-gate } 24020Sstevel@tonic-gate 24030Sstevel@tonic-gate #define QUERY_BASE 128 24040Sstevel@tonic-gate backend_query_t * 24050Sstevel@tonic-gate backend_query_alloc(void) 24060Sstevel@tonic-gate { 24070Sstevel@tonic-gate backend_query_t *q; 24080Sstevel@tonic-gate q = calloc(1, sizeof (backend_query_t)); 24090Sstevel@tonic-gate if (q != NULL) { 24100Sstevel@tonic-gate q->bq_size = QUERY_BASE; 24110Sstevel@tonic-gate q->bq_buf = calloc(1, q->bq_size); 24120Sstevel@tonic-gate if (q->bq_buf == NULL) { 24130Sstevel@tonic-gate q->bq_size = 0; 24140Sstevel@tonic-gate } 24150Sstevel@tonic-gate 24160Sstevel@tonic-gate } 24170Sstevel@tonic-gate return (q); 24180Sstevel@tonic-gate } 24190Sstevel@tonic-gate 24200Sstevel@tonic-gate void 24210Sstevel@tonic-gate backend_query_append(backend_query_t *q, const char *value) 24220Sstevel@tonic-gate { 24230Sstevel@tonic-gate char *alloc; 24240Sstevel@tonic-gate int count; 24250Sstevel@tonic-gate size_t size, old_len; 24260Sstevel@tonic-gate 24270Sstevel@tonic-gate if (q == NULL) { 24280Sstevel@tonic-gate /* We'll discover the error when we try to run the query. */ 24290Sstevel@tonic-gate return; 24300Sstevel@tonic-gate } 24310Sstevel@tonic-gate 24320Sstevel@tonic-gate while (q->bq_buf != NULL) { 24330Sstevel@tonic-gate old_len = strlen(q->bq_buf); 24340Sstevel@tonic-gate size = q->bq_size; 24350Sstevel@tonic-gate count = strlcat(q->bq_buf, value, size); 24360Sstevel@tonic-gate 24370Sstevel@tonic-gate if (count < size) 24380Sstevel@tonic-gate break; /* success */ 24390Sstevel@tonic-gate 24400Sstevel@tonic-gate q->bq_buf[old_len] = 0; 24410Sstevel@tonic-gate size = round_up_to_p2(count + 1); 24420Sstevel@tonic-gate 24430Sstevel@tonic-gate assert(size > q->bq_size); 24440Sstevel@tonic-gate alloc = realloc(q->bq_buf, size); 24450Sstevel@tonic-gate if (alloc == NULL) { 24460Sstevel@tonic-gate free(q->bq_buf); 24470Sstevel@tonic-gate q->bq_buf = NULL; 24480Sstevel@tonic-gate break; /* can't grow */ 24490Sstevel@tonic-gate } 24500Sstevel@tonic-gate 24510Sstevel@tonic-gate q->bq_buf = alloc; 24520Sstevel@tonic-gate q->bq_size = size; 24530Sstevel@tonic-gate } 24540Sstevel@tonic-gate } 24550Sstevel@tonic-gate 24560Sstevel@tonic-gate void 24570Sstevel@tonic-gate backend_query_add(backend_query_t *q, const char *format, ...) 24580Sstevel@tonic-gate { 24590Sstevel@tonic-gate va_list args; 24600Sstevel@tonic-gate char *new; 24610Sstevel@tonic-gate 24620Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 24630Sstevel@tonic-gate return; 24640Sstevel@tonic-gate 24650Sstevel@tonic-gate va_start(args, format); 24660Sstevel@tonic-gate new = sqlite_vmprintf(format, args); 24670Sstevel@tonic-gate va_end(args); 24680Sstevel@tonic-gate 24690Sstevel@tonic-gate if (new == NULL) { 24700Sstevel@tonic-gate free(q->bq_buf); 24710Sstevel@tonic-gate q->bq_buf = NULL; 24720Sstevel@tonic-gate return; 24730Sstevel@tonic-gate } 24740Sstevel@tonic-gate 24750Sstevel@tonic-gate backend_query_append(q, new); 24760Sstevel@tonic-gate 24770Sstevel@tonic-gate free(new); 24780Sstevel@tonic-gate } 24790Sstevel@tonic-gate 24800Sstevel@tonic-gate void 24810Sstevel@tonic-gate backend_query_free(backend_query_t *q) 24820Sstevel@tonic-gate { 24830Sstevel@tonic-gate if (q != NULL) { 24840Sstevel@tonic-gate if (q->bq_buf != NULL) { 24850Sstevel@tonic-gate free(q->bq_buf); 24860Sstevel@tonic-gate } 24870Sstevel@tonic-gate free(q); 24880Sstevel@tonic-gate } 24890Sstevel@tonic-gate } 2490