17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5c5c4113dSnw141292 * Common Development and Distribution License (the "License"). 6c5c4113dSnw141292 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 215b7f77adStw21770 227c478bd9Sstevel@tonic-gate /* 239444c26fSTom Whitten * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 278918dff3Sjwadams /* 288918dff3Sjwadams * sqlite is not compatible with _FILE_OFFSET_BITS=64, but we need to 298918dff3Sjwadams * be able to statvfs(2) possibly large systems. This define gives us 308918dff3Sjwadams * access to the transitional interfaces. See lfcompile64(5) for how 318918dff3Sjwadams * _LARGEFILE64_SOURCE works. 328918dff3Sjwadams */ 338918dff3Sjwadams #define _LARGEFILE64_SOURCE 348918dff3Sjwadams 357c478bd9Sstevel@tonic-gate #include <assert.h> 369444c26fSTom Whitten #include <atomic.h> 377c478bd9Sstevel@tonic-gate #include <door.h> 387c478bd9Sstevel@tonic-gate #include <dirent.h> 397c478bd9Sstevel@tonic-gate #include <errno.h> 407c478bd9Sstevel@tonic-gate #include <fcntl.h> 417c478bd9Sstevel@tonic-gate #include <limits.h> 427c478bd9Sstevel@tonic-gate #include <pthread.h> 437c478bd9Sstevel@tonic-gate #include <stdarg.h> 447c478bd9Sstevel@tonic-gate #include <stdio.h> 457c478bd9Sstevel@tonic-gate #include <stdlib.h> 467c478bd9Sstevel@tonic-gate #include <string.h> 479444c26fSTom Whitten #include <strings.h> 488918dff3Sjwadams #include <sys/stat.h> 498918dff3Sjwadams #include <sys/statvfs.h> 509444c26fSTom Whitten #include <time.h> 517c478bd9Sstevel@tonic-gate #include <unistd.h> 527c478bd9Sstevel@tonic-gate #include <zone.h> 53c0889d7aSstevep #include <libscf_priv.h> 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate #include "configd.h" 567c478bd9Sstevel@tonic-gate #include "repcache_protocol.h" 577c478bd9Sstevel@tonic-gate 58c5c4113dSnw141292 #include <sqlite.h> 59c5c4113dSnw141292 #include <sqlite-misc.h> 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate /* 627c478bd9Sstevel@tonic-gate * This file has two purposes: 637c478bd9Sstevel@tonic-gate * 647c478bd9Sstevel@tonic-gate * 1. It contains the database schema, and the code for setting up our backend 657c478bd9Sstevel@tonic-gate * databases, including installing said schema. 667c478bd9Sstevel@tonic-gate * 677c478bd9Sstevel@tonic-gate * 2. It provides a simplified interface to the SQL database library, and 687c478bd9Sstevel@tonic-gate * synchronizes MT access to the database. 697c478bd9Sstevel@tonic-gate */ 707c478bd9Sstevel@tonic-gate 719444c26fSTom Whitten #define IS_VOLATILE(be) ((be)->be_ppath != NULL) 729444c26fSTom Whitten #define MAX_FLIGHT_RECORDER_EVENTS 100 739444c26fSTom Whitten 749444c26fSTom Whitten typedef enum backend_switch_results { 759444c26fSTom Whitten BACKEND_SWITCH_FATAL = -1, 769444c26fSTom Whitten BACKEND_SWITCH_OK = 0, 779444c26fSTom Whitten BACKEND_SWITCH_RO 789444c26fSTom Whitten } backend_switch_results_t; 799444c26fSTom Whitten 807c478bd9Sstevel@tonic-gate typedef struct backend_spent { 817c478bd9Sstevel@tonic-gate uint64_t bs_count; 827c478bd9Sstevel@tonic-gate hrtime_t bs_time; 837c478bd9Sstevel@tonic-gate hrtime_t bs_vtime; 847c478bd9Sstevel@tonic-gate } backend_spent_t; 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate typedef struct backend_totals { 877c478bd9Sstevel@tonic-gate backend_spent_t bt_lock; /* waiting for lock */ 887c478bd9Sstevel@tonic-gate backend_spent_t bt_exec; /* time spent executing SQL */ 897c478bd9Sstevel@tonic-gate } backend_totals_t; 907c478bd9Sstevel@tonic-gate 919444c26fSTom Whitten /* 929444c26fSTom Whitten * There are times when svcadm asks configd to move the BACKEND_TYPE_NORMAL 939444c26fSTom Whitten * repository to volatile storage. See backend_switch(). When the 949444c26fSTom Whitten * repository is on volatile storage, we save the location of the permanent 959444c26fSTom Whitten * repository in be_ppath. We use the saved path when the time comes to 969444c26fSTom Whitten * move the repository back. When the repository is on permanent storage, 979444c26fSTom Whitten * be_ppath is set to NULL. Also see the definition of IS_VOLATILE() above 989444c26fSTom Whitten * for testing if the repository is on volatile storage. 999444c26fSTom Whitten */ 1007c478bd9Sstevel@tonic-gate typedef struct sqlite_backend { 1017c478bd9Sstevel@tonic-gate pthread_mutex_t be_lock; 1027c478bd9Sstevel@tonic-gate pthread_t be_thread; /* thread holding lock */ 1037c478bd9Sstevel@tonic-gate struct sqlite *be_db; 1047c478bd9Sstevel@tonic-gate const char *be_path; /* path to db */ 1059444c26fSTom Whitten const char *be_ppath; /* saved path to persistent db when */ 1069444c26fSTom Whitten /* backend is volatile */ 1079444c26fSTom Whitten const char *be_checkpoint; /* path to repository checkpoint */ 1088918dff3Sjwadams int be_readonly; /* readonly at start, and still is */ 1097c478bd9Sstevel@tonic-gate int be_writing; /* held for writing */ 1107c478bd9Sstevel@tonic-gate backend_type_t be_type; /* type of db */ 1118918dff3Sjwadams hrtime_t be_lastcheck; /* time of last read-only check */ 1127c478bd9Sstevel@tonic-gate backend_totals_t be_totals[2]; /* one for reading, one for writing */ 1137c478bd9Sstevel@tonic-gate } sqlite_backend_t; 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate struct backend_tx { 1167c478bd9Sstevel@tonic-gate sqlite_backend_t *bt_be; 1177c478bd9Sstevel@tonic-gate int bt_readonly; 1187c478bd9Sstevel@tonic-gate int bt_type; 1197c478bd9Sstevel@tonic-gate int bt_full; /* SQLITE_FULL during tx */ 1207c478bd9Sstevel@tonic-gate }; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate #define UPDATE_TOTALS_WR(sb, writing, field, ts, vts) { \ 1237c478bd9Sstevel@tonic-gate backend_spent_t *__bsp = &(sb)->be_totals[!!(writing)].field; \ 1247c478bd9Sstevel@tonic-gate __bsp->bs_count++; \ 1257c478bd9Sstevel@tonic-gate __bsp->bs_time += (gethrtime() - ts); \ 1267c478bd9Sstevel@tonic-gate __bsp->bs_vtime += (gethrvtime() - vts); \ 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate #define UPDATE_TOTALS(sb, field, ts, vts) \ 1307c478bd9Sstevel@tonic-gate UPDATE_TOTALS_WR(sb, (sb)->be_writing, field, ts, vts) 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate struct backend_query { 1337c478bd9Sstevel@tonic-gate char *bq_buf; 1347c478bd9Sstevel@tonic-gate size_t bq_size; 1357c478bd9Sstevel@tonic-gate }; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate struct backend_tbl_info { 1387c478bd9Sstevel@tonic-gate const char *bti_name; 1397c478bd9Sstevel@tonic-gate const char *bti_cols; 1407c478bd9Sstevel@tonic-gate }; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate struct backend_idx_info { 1437c478bd9Sstevel@tonic-gate const char *bxi_tbl; 1447c478bd9Sstevel@tonic-gate const char *bxi_idx; 1457c478bd9Sstevel@tonic-gate const char *bxi_cols; 1467c478bd9Sstevel@tonic-gate }; 1477c478bd9Sstevel@tonic-gate 1489444c26fSTom Whitten /* Definitions for the flight recorder: */ 1499444c26fSTom Whitten 1509444c26fSTom Whitten typedef enum be_flight_type { 1519444c26fSTom Whitten BE_FLIGHT_EV_NOEVENT = 0, /* No event yet recorded. */ 1529444c26fSTom Whitten BE_FLIGHT_EV_BACKUP, /* Information about repo. backup */ 1539444c26fSTom Whitten BE_FLIGHT_EV_BACKUP_ENTER, /* Enter */ 1549444c26fSTom Whitten /* backend_create_backup_locked() */ 1559444c26fSTom Whitten BE_FLIGHT_EV_CHECKPOINT, /* Request to checkpoint repository */ 1569444c26fSTom Whitten /* for boot time backup */ 1579444c26fSTom Whitten BE_FLIGHT_EV_CHECKPOINT_EXISTS, /* Existing checkpoint detected on */ 1589444c26fSTom Whitten /* restart */ 1599444c26fSTom Whitten BE_FLIGHT_EV_LINGERING_FAST, /* Use lingering fast repository */ 1609444c26fSTom Whitten BE_FLIGHT_EV_NO_BACKUP, /* Requested backup not made */ 1619444c26fSTom Whitten BE_FLIGHT_EV_REPO_CREATE, /* Main repository created */ 1629444c26fSTom Whitten BE_FLIGHT_EV_RESTART, /* This is a restart of configd */ 1639444c26fSTom Whitten BE_FLIGHT_EV_SWITCH, /* Switch repositories */ 1649444c26fSTom Whitten BE_FLIGHT_EV_TRANS_RW /* Root transitioned to read/write */ 1659444c26fSTom Whitten } be_flight_type_t; 1669444c26fSTom Whitten 1679444c26fSTom Whitten typedef enum be_flight_status { 1689444c26fSTom Whitten BE_FLIGHT_ST_INFO = 0, /* No status. Event is informative */ 1699444c26fSTom Whitten BE_FLIGHT_ST_BOOT_BACKUP, /* Boot time backup */ 1709444c26fSTom Whitten BE_FLIGHT_ST_CHECKPOINT_BACKUP, /* Backup from checkpoint */ 1719444c26fSTom Whitten BE_FLIGHT_ST_CLIENT, /* Request form client as opposed to */ 1729444c26fSTom Whitten /* internal call */ 1739444c26fSTom Whitten BE_FLIGHT_ST_DUPLICATE, /* Backup duplicates existing one */ 1749444c26fSTom Whitten BE_FLIGHT_ST_FAIL, /* Operation failed. */ 1759444c26fSTom Whitten BE_FLIGHT_ST_FAST, /* Fast repository (tmpfs) */ 1769444c26fSTom Whitten BE_FLIGHT_ST_MI_BACKUP, /* Manifest-import backup */ 1779444c26fSTom Whitten BE_FLIGHT_ST_NO_SWITCH, /* Don't switch repositories */ 1789444c26fSTom Whitten BE_FLIGHT_ST_OTHER_BACKUP, /* Other type of backup */ 1799444c26fSTom Whitten BE_FLIGHT_ST_PERMANENT, /* Repository on permanet storage */ 1809444c26fSTom Whitten BE_FLIGHT_ST_REPO_BACKUP, /* Backup from repository */ 1819444c26fSTom Whitten BE_FLIGHT_ST_RO, /* Main repository is read-only */ 1829444c26fSTom Whitten BE_FLIGHT_ST_RW, /* Main repository is read/write */ 1839444c26fSTom Whitten BE_FLIGHT_ST_SUCCESS, /* Operation was successful */ 1849444c26fSTom Whitten BE_FLIGHT_ST_SWITCH /* Switch repository */ 1859444c26fSTom Whitten } be_flight_status_t; 1869444c26fSTom Whitten 1879444c26fSTom Whitten typedef struct be_flight_event { 1889444c26fSTom Whitten be_flight_type_t bfe_type; /* Type of event. */ 1899444c26fSTom Whitten be_flight_status_t bfe_status; /* Result of the event. */ 1909444c26fSTom Whitten time_t bfe_time; /* Time of the event. */ 1919444c26fSTom Whitten uint_t bfe_sequence; /* Sequence number. */ 1929444c26fSTom Whitten } be_flight_event_t; 1939444c26fSTom Whitten 1947c478bd9Sstevel@tonic-gate static pthread_mutex_t backend_panic_lock = PTHREAD_MUTEX_INITIALIZER; 1957c478bd9Sstevel@tonic-gate static pthread_cond_t backend_panic_cv = PTHREAD_COND_INITIALIZER; 1967c478bd9Sstevel@tonic-gate pthread_t backend_panic_thread = 0; 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate int backend_do_trace = 0; /* invoke tracing callback */ 1997c478bd9Sstevel@tonic-gate int backend_print_trace = 0; /* tracing callback prints SQL */ 2007c478bd9Sstevel@tonic-gate int backend_panic_abort = 0; /* abort when panicking */ 2017c478bd9Sstevel@tonic-gate 2029444c26fSTom Whitten /* Data for the flight_recorder. */ 2039444c26fSTom Whitten 2049444c26fSTom Whitten static pthread_mutex_t backend_flight_recorder_lock = PTHREAD_MUTEX_INITIALIZER; 2059444c26fSTom Whitten static be_flight_event_t flight_recorder[MAX_FLIGHT_RECORDER_EVENTS]; 2069444c26fSTom Whitten static uint_t flight_recorder_next = 0; 2079444c26fSTom Whitten static uint_t flight_recorder_missed = 0; 2089444c26fSTom Whitten static uint_t flight_recorder_sequence = 0; 2099444c26fSTom Whitten 2108918dff3Sjwadams /* interval between read-only checks while starting up */ 2118918dff3Sjwadams #define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC) 2128918dff3Sjwadams 2137c478bd9Sstevel@tonic-gate /* 2146e1d2b42Samaguire * Any incompatible change to the below schema should bump the version number. 2156e1d2b42Samaguire * The schema has been changed to support value ordering, but this change 2166e1d2b42Samaguire * is backwards-compatible - i.e. a previous svc.configd can use a 2176e1d2b42Samaguire * repository database with the new schema perfectly well. As a result, 2186e1d2b42Samaguire * the schema version has not been updated, allowing downgrade of systems 2196e1d2b42Samaguire * without losing repository data. 2207c478bd9Sstevel@tonic-gate */ 2217c478bd9Sstevel@tonic-gate #define BACKEND_SCHEMA_VERSION 5 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_normal[] = { /* BACKEND_TYPE_NORMAL */ 2247c478bd9Sstevel@tonic-gate /* 2257c478bd9Sstevel@tonic-gate * service_tbl holds all services. svc_id is the identifier of the 2267c478bd9Sstevel@tonic-gate * service. 2277c478bd9Sstevel@tonic-gate */ 2287c478bd9Sstevel@tonic-gate { 2297c478bd9Sstevel@tonic-gate "service_tbl", 2307c478bd9Sstevel@tonic-gate "svc_id INTEGER PRIMARY KEY," 2317c478bd9Sstevel@tonic-gate "svc_name CHAR(256) NOT NULL" 2327c478bd9Sstevel@tonic-gate }, 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate /* 2357c478bd9Sstevel@tonic-gate * instance_tbl holds all of the instances. The parent service id 2367c478bd9Sstevel@tonic-gate * is instance_svc. 2377c478bd9Sstevel@tonic-gate */ 2387c478bd9Sstevel@tonic-gate { 2397c478bd9Sstevel@tonic-gate "instance_tbl", 2407c478bd9Sstevel@tonic-gate "instance_id INTEGER PRIMARY KEY," 2417c478bd9Sstevel@tonic-gate "instance_name CHAR(256) NOT NULL," 2427c478bd9Sstevel@tonic-gate "instance_svc INTEGER NOT NULL" 2437c478bd9Sstevel@tonic-gate }, 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate /* 2467c478bd9Sstevel@tonic-gate * snapshot_lnk_tbl links (instance, snapshot name) with snapshots. 2477c478bd9Sstevel@tonic-gate */ 2487c478bd9Sstevel@tonic-gate { 2497c478bd9Sstevel@tonic-gate "snapshot_lnk_tbl", 2507c478bd9Sstevel@tonic-gate "lnk_id INTEGER PRIMARY KEY," 2517c478bd9Sstevel@tonic-gate "lnk_inst_id INTEGER NOT NULL," 2527c478bd9Sstevel@tonic-gate "lnk_snap_name CHAR(256) NOT NULL," 2537c478bd9Sstevel@tonic-gate "lnk_snap_id INTEGER NOT NULL" 2547c478bd9Sstevel@tonic-gate }, 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate /* 2577c478bd9Sstevel@tonic-gate * snaplevel_tbl maps a snapshot id to a set of named, ordered 2587c478bd9Sstevel@tonic-gate * snaplevels. 2597c478bd9Sstevel@tonic-gate */ 2607c478bd9Sstevel@tonic-gate { 2617c478bd9Sstevel@tonic-gate "snaplevel_tbl", 2627c478bd9Sstevel@tonic-gate "snap_id INTEGER NOT NULL," 2637c478bd9Sstevel@tonic-gate "snap_level_num INTEGER NOT NULL," 2647c478bd9Sstevel@tonic-gate "snap_level_id INTEGER NOT NULL," 2657c478bd9Sstevel@tonic-gate "snap_level_service_id INTEGER NOT NULL," 2667c478bd9Sstevel@tonic-gate "snap_level_service CHAR(256) NOT NULL," 2677c478bd9Sstevel@tonic-gate "snap_level_instance_id INTEGER NULL," 2687c478bd9Sstevel@tonic-gate "snap_level_instance CHAR(256) NULL" 2697c478bd9Sstevel@tonic-gate }, 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate /* 2727c478bd9Sstevel@tonic-gate * snaplevel_lnk_tbl links snaplevels to property groups. 2737c478bd9Sstevel@tonic-gate * snaplvl_pg_* is identical to the original property group, 2747c478bd9Sstevel@tonic-gate * and snaplvl_gen_id overrides the generation number. 2757c478bd9Sstevel@tonic-gate * The service/instance ids are as in the snaplevel. 2767c478bd9Sstevel@tonic-gate */ 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate "snaplevel_lnk_tbl", 2797c478bd9Sstevel@tonic-gate "snaplvl_level_id INTEGER NOT NULL," 2807c478bd9Sstevel@tonic-gate "snaplvl_pg_id INTEGER NOT NULL," 2817c478bd9Sstevel@tonic-gate "snaplvl_pg_name CHAR(256) NOT NULL," 2827c478bd9Sstevel@tonic-gate "snaplvl_pg_type CHAR(256) NOT NULL," 2837c478bd9Sstevel@tonic-gate "snaplvl_pg_flags INTEGER NOT NULL," 2847c478bd9Sstevel@tonic-gate "snaplvl_gen_id INTEGER NOT NULL" 2857c478bd9Sstevel@tonic-gate }, 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate { NULL, NULL } 2887c478bd9Sstevel@tonic-gate }; 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_normal[] = { /* BACKEND_TYPE_NORMAL */ 2917c478bd9Sstevel@tonic-gate { "service_tbl", "name", "svc_name" }, 2927c478bd9Sstevel@tonic-gate { "instance_tbl", "name", "instance_svc, instance_name" }, 2937c478bd9Sstevel@tonic-gate { "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" }, 2947c478bd9Sstevel@tonic-gate { "snapshot_lnk_tbl", "snapid", "lnk_snap_id" }, 2957c478bd9Sstevel@tonic-gate { "snaplevel_tbl", "id", "snap_id" }, 2967c478bd9Sstevel@tonic-gate { "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" }, 2977c478bd9Sstevel@tonic-gate { "snaplevel_lnk_tbl", "level", "snaplvl_level_id" }, 2987c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 2997c478bd9Sstevel@tonic-gate }; 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_np[] = { /* BACKEND_TYPE_NONPERSIST */ 3027c478bd9Sstevel@tonic-gate { NULL, NULL } 3037c478bd9Sstevel@tonic-gate }; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_np[] = { /* BACKEND_TYPE_NONPERSIST */ 3067c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 3077c478bd9Sstevel@tonic-gate }; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_common[] = { /* all backend types */ 3107c478bd9Sstevel@tonic-gate /* 3117c478bd9Sstevel@tonic-gate * pg_tbl defines property groups. They are associated with a single 3127c478bd9Sstevel@tonic-gate * service or instance. The pg_gen_id links them with the latest 3137c478bd9Sstevel@tonic-gate * "edited" version of its properties. 3147c478bd9Sstevel@tonic-gate */ 3157c478bd9Sstevel@tonic-gate { 3167c478bd9Sstevel@tonic-gate "pg_tbl", 3177c478bd9Sstevel@tonic-gate "pg_id INTEGER PRIMARY KEY," 3187c478bd9Sstevel@tonic-gate "pg_parent_id INTEGER NOT NULL," 3197c478bd9Sstevel@tonic-gate "pg_name CHAR(256) NOT NULL," 3207c478bd9Sstevel@tonic-gate "pg_type CHAR(256) NOT NULL," 3217c478bd9Sstevel@tonic-gate "pg_flags INTEGER NOT NULL," 3227c478bd9Sstevel@tonic-gate "pg_gen_id INTEGER NOT NULL" 3237c478bd9Sstevel@tonic-gate }, 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate /* 3267c478bd9Sstevel@tonic-gate * prop_lnk_tbl links a particular pg_id and gen_id to a set of 3277c478bd9Sstevel@tonic-gate * (prop_name, prop_type, val_id) trios. 3287c478bd9Sstevel@tonic-gate */ 3297c478bd9Sstevel@tonic-gate { 3307c478bd9Sstevel@tonic-gate "prop_lnk_tbl", 3317c478bd9Sstevel@tonic-gate "lnk_prop_id INTEGER PRIMARY KEY," 3327c478bd9Sstevel@tonic-gate "lnk_pg_id INTEGER NOT NULL," 3337c478bd9Sstevel@tonic-gate "lnk_gen_id INTEGER NOT NULL," 3347c478bd9Sstevel@tonic-gate "lnk_prop_name CHAR(256) NOT NULL," 3357c478bd9Sstevel@tonic-gate "lnk_prop_type CHAR(2) NOT NULL," 3367c478bd9Sstevel@tonic-gate "lnk_val_id INTEGER" 3377c478bd9Sstevel@tonic-gate }, 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate /* 3407c478bd9Sstevel@tonic-gate * value_tbl maps a value_id to a set of values. For any given 3416e1d2b42Samaguire * value_id, value_type is constant. The table definition here 3426e1d2b42Samaguire * is repeated in backend_check_upgrade(), and must be kept in-sync. 3437c478bd9Sstevel@tonic-gate */ 3447c478bd9Sstevel@tonic-gate { 3457c478bd9Sstevel@tonic-gate "value_tbl", 3467c478bd9Sstevel@tonic-gate "value_id INTEGER NOT NULL," 3477c478bd9Sstevel@tonic-gate "value_type CHAR(1) NOT NULL," 3486e1d2b42Samaguire "value_value VARCHAR NOT NULL," 3496e1d2b42Samaguire "value_order INTEGER DEFAULT 0" 3507c478bd9Sstevel@tonic-gate }, 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate /* 3537c478bd9Sstevel@tonic-gate * id_tbl has one row per id space 3547c478bd9Sstevel@tonic-gate */ 3557c478bd9Sstevel@tonic-gate { 3567c478bd9Sstevel@tonic-gate "id_tbl", 3577c478bd9Sstevel@tonic-gate "id_name STRING NOT NULL," 3587c478bd9Sstevel@tonic-gate "id_next INTEGER NOT NULL" 3597c478bd9Sstevel@tonic-gate }, 3607c478bd9Sstevel@tonic-gate 3617c478bd9Sstevel@tonic-gate /* 3627c478bd9Sstevel@tonic-gate * schema_version has a single row, which contains 3637c478bd9Sstevel@tonic-gate * BACKEND_SCHEMA_VERSION at the time of creation. 3647c478bd9Sstevel@tonic-gate */ 3657c478bd9Sstevel@tonic-gate { 3667c478bd9Sstevel@tonic-gate "schema_version", 3677c478bd9Sstevel@tonic-gate "schema_version INTEGER" 3687c478bd9Sstevel@tonic-gate }, 3697c478bd9Sstevel@tonic-gate { NULL, NULL } 3707c478bd9Sstevel@tonic-gate }; 3717c478bd9Sstevel@tonic-gate 3726e1d2b42Samaguire /* 3736e1d2b42Samaguire * The indexing of value_tbl is repeated in backend_check_upgrade() and 3746e1d2b42Samaguire * must be kept in sync with the indexing specification here. 3756e1d2b42Samaguire */ 3767c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_common[] = { /* all backend types */ 3777c478bd9Sstevel@tonic-gate { "pg_tbl", "parent", "pg_parent_id" }, 3787c478bd9Sstevel@tonic-gate { "pg_tbl", "name", "pg_parent_id, pg_name" }, 3797c478bd9Sstevel@tonic-gate { "pg_tbl", "type", "pg_parent_id, pg_type" }, 3807c478bd9Sstevel@tonic-gate { "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" }, 3817c478bd9Sstevel@tonic-gate { "prop_lnk_tbl", "val", "lnk_val_id" }, 3827c478bd9Sstevel@tonic-gate { "value_tbl", "id", "value_id" }, 3837c478bd9Sstevel@tonic-gate { "id_tbl", "id", "id_name" }, 3847c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 3857c478bd9Sstevel@tonic-gate }; 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate struct run_single_int_info { 3887c478bd9Sstevel@tonic-gate uint32_t *rs_out; 3897c478bd9Sstevel@tonic-gate int rs_result; 3907c478bd9Sstevel@tonic-gate }; 3917c478bd9Sstevel@tonic-gate 3929444c26fSTom Whitten static rep_protocol_responseid_t backend_copy_repository(const char *, 3939444c26fSTom Whitten const char *, int); 3949444c26fSTom Whitten static rep_protocol_responseid_t backend_do_copy(const char *, int, 3959444c26fSTom Whitten const char *, int, size_t *); 3969444c26fSTom Whitten 3979444c26fSTom Whitten /* 3989444c26fSTom Whitten * The flight recorder keeps track of events that happen primarily while 3999444c26fSTom Whitten * the system is booting. Once the system is up an running, one can take a 4009444c26fSTom Whitten * gcore(1) of configd and examine the events with mdb. Since we're most 4019444c26fSTom Whitten * interested in early boot events, we stop recording events when the 4029444c26fSTom Whitten * recorder is full. 4039444c26fSTom Whitten */ 4049444c26fSTom Whitten static void 4059444c26fSTom Whitten flight_recorder_event(be_flight_type_t type, be_flight_status_t res) 4069444c26fSTom Whitten { 4079444c26fSTom Whitten be_flight_event_t *data; 4089444c26fSTom Whitten uint_t item; 4099444c26fSTom Whitten uint_t sequence; 4109444c26fSTom Whitten 4119444c26fSTom Whitten if (pthread_mutex_lock(&backend_flight_recorder_lock) != 0) { 4129444c26fSTom Whitten atomic_inc_uint(&flight_recorder_missed); 4139444c26fSTom Whitten return; 4149444c26fSTom Whitten } 4159444c26fSTom Whitten if (flight_recorder_next >= MAX_FLIGHT_RECORDER_EVENTS) { 4169444c26fSTom Whitten /* Hit end of the array. No more event recording. */ 4179444c26fSTom Whitten item = flight_recorder_next; 4189444c26fSTom Whitten } else { 4199444c26fSTom Whitten item = flight_recorder_next++; 4209444c26fSTom Whitten sequence = flight_recorder_sequence++; 4219444c26fSTom Whitten } 4229444c26fSTom Whitten (void) pthread_mutex_unlock(&backend_flight_recorder_lock); 4239444c26fSTom Whitten 4249444c26fSTom Whitten if (item >= MAX_FLIGHT_RECORDER_EVENTS) { 4259444c26fSTom Whitten /* Array is filled. Stop recording events */ 4269444c26fSTom Whitten atomic_inc_uint(&flight_recorder_missed); 4279444c26fSTom Whitten return; 4289444c26fSTom Whitten } 4299444c26fSTom Whitten data = &flight_recorder[item]; 4309444c26fSTom Whitten (void) memset(data, 0, sizeof (*data)); 4319444c26fSTom Whitten data->bfe_type = type; 4329444c26fSTom Whitten data->bfe_status = res; 4339444c26fSTom Whitten data->bfe_sequence = sequence; 4349444c26fSTom Whitten data->bfe_time = time(NULL); 4359444c26fSTom Whitten } 4369444c26fSTom Whitten 4377c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4387c478bd9Sstevel@tonic-gate static int 4397c478bd9Sstevel@tonic-gate run_single_int_callback(void *arg, int columns, char **vals, char **names) 4407c478bd9Sstevel@tonic-gate { 4417c478bd9Sstevel@tonic-gate struct run_single_int_info *info = arg; 4427c478bd9Sstevel@tonic-gate uint32_t val; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate char *endptr = vals[0]; 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate assert(info->rs_result != REP_PROTOCOL_SUCCESS); 4477c478bd9Sstevel@tonic-gate assert(columns == 1); 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate if (vals[0] == NULL) 4507c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate errno = 0; 4537c478bd9Sstevel@tonic-gate val = strtoul(vals[0], &endptr, 10); 4547c478bd9Sstevel@tonic-gate if ((val == 0 && endptr == vals[0]) || *endptr != 0 || errno != 0) 4557c478bd9Sstevel@tonic-gate backend_panic("malformed integer \"%20s\"", vals[0]); 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate *info->rs_out = val; 4587c478bd9Sstevel@tonic-gate info->rs_result = REP_PROTOCOL_SUCCESS; 4597c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4637c478bd9Sstevel@tonic-gate int 4647c478bd9Sstevel@tonic-gate backend_fail_if_seen(void *arg, int columns, char **vals, char **names) 4657c478bd9Sstevel@tonic-gate { 4667c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4698918dff3Sjwadams /* 4708918dff3Sjwadams * check to see if we can successfully start a transaction; if not, the 4718918dff3Sjwadams * filesystem is mounted read-only. 4728918dff3Sjwadams */ 4737c478bd9Sstevel@tonic-gate static int 4748918dff3Sjwadams backend_is_readonly(struct sqlite *db, const char *path) 4757c478bd9Sstevel@tonic-gate { 4768918dff3Sjwadams int r; 4778918dff3Sjwadams statvfs64_t stat; 4788918dff3Sjwadams 4798918dff3Sjwadams if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY)) 4808918dff3Sjwadams return (SQLITE_READONLY); 4818918dff3Sjwadams 4828918dff3Sjwadams r = sqlite_exec(db, 4837c478bd9Sstevel@tonic-gate "BEGIN TRANSACTION; " 4847c478bd9Sstevel@tonic-gate "UPDATE schema_version SET schema_version = schema_version; ", 4858918dff3Sjwadams NULL, NULL, NULL); 4867c478bd9Sstevel@tonic-gate (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 4877c478bd9Sstevel@tonic-gate return (r); 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate static void 4917c478bd9Sstevel@tonic-gate backend_trace_sql(void *arg, const char *sql) 4927c478bd9Sstevel@tonic-gate { 4937c478bd9Sstevel@tonic-gate sqlite_backend_t *be = arg; 4947c478bd9Sstevel@tonic-gate 4957c478bd9Sstevel@tonic-gate if (backend_print_trace) { 4967c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d: %s\n", be->be_type, sql); 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; 5017c478bd9Sstevel@tonic-gate static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; 5027c478bd9Sstevel@tonic-gate 5036e1d2b42Samaguire /* 5046e1d2b42Samaguire * For a native build, repositories are created from scratch, so upgrade 5056e1d2b42Samaguire * is not an issue. This variable is implicitly protected by 5066e1d2b42Samaguire * bes[BACKEND_TYPE_NORMAL]->be_lock. 5076e1d2b42Samaguire */ 5086e1d2b42Samaguire #ifdef NATIVE_BUILD 5096e1d2b42Samaguire static boolean_t be_normal_upgraded = B_TRUE; 5106e1d2b42Samaguire #else 5116e1d2b42Samaguire static boolean_t be_normal_upgraded = B_FALSE; 5126e1d2b42Samaguire #endif /* NATIVE_BUILD */ 5136e1d2b42Samaguire 5146e1d2b42Samaguire /* 5156e1d2b42Samaguire * Has backend been upgraded? In nonpersistent case, answer is always 5166e1d2b42Samaguire * yes. 5176e1d2b42Samaguire */ 5186e1d2b42Samaguire boolean_t 5196e1d2b42Samaguire backend_is_upgraded(backend_tx_t *bt) 5206e1d2b42Samaguire { 5216e1d2b42Samaguire if (bt->bt_type == BACKEND_TYPE_NONPERSIST) 5226e1d2b42Samaguire return (B_TRUE); 5236e1d2b42Samaguire return (be_normal_upgraded); 5246e1d2b42Samaguire } 5256e1d2b42Samaguire 5267c478bd9Sstevel@tonic-gate #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) 5277c478bd9Sstevel@tonic-gate /* 5287c478bd9Sstevel@tonic-gate * backend_panic() -- some kind of database problem or corruption has been hit. 5297c478bd9Sstevel@tonic-gate * We attempt to quiesce the other database users -- all of the backend sql 5307c478bd9Sstevel@tonic-gate * entry points will call backend_panic(NULL) if a panic is in progress, as 5317c478bd9Sstevel@tonic-gate * will any attempt to start a transaction. 5327c478bd9Sstevel@tonic-gate * 5337c478bd9Sstevel@tonic-gate * We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to 5347c478bd9Sstevel@tonic-gate * either drop the lock or call backend_panic(). If they don't respond in 5357c478bd9Sstevel@tonic-gate * time, we'll just exit anyway. 5367c478bd9Sstevel@tonic-gate */ 5377c478bd9Sstevel@tonic-gate void 5387c478bd9Sstevel@tonic-gate backend_panic(const char *format, ...) 5397c478bd9Sstevel@tonic-gate { 5407c478bd9Sstevel@tonic-gate int i; 5417c478bd9Sstevel@tonic-gate va_list args; 5427c478bd9Sstevel@tonic-gate int failed = 0; 5437c478bd9Sstevel@tonic-gate 5447c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 5457c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) { 5467c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 5477c478bd9Sstevel@tonic-gate /* 5487c478bd9Sstevel@tonic-gate * first, drop any backend locks we're holding, then 5497c478bd9Sstevel@tonic-gate * sleep forever on the panic_cv. 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 5527c478bd9Sstevel@tonic-gate if (bes[i] != NULL && 5537c478bd9Sstevel@tonic-gate bes[i]->be_thread == pthread_self()) 5547c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 5557c478bd9Sstevel@tonic-gate } 5567c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 5577c478bd9Sstevel@tonic-gate for (;;) 5587c478bd9Sstevel@tonic-gate (void) pthread_cond_wait(&backend_panic_cv, 5597c478bd9Sstevel@tonic-gate &backend_panic_lock); 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate backend_panic_thread = pthread_self(); 5627c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 5657c478bd9Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread == pthread_self()) 5667c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate va_start(args, format); 5707c478bd9Sstevel@tonic-gate configd_vcritical(format, args); 5717c478bd9Sstevel@tonic-gate va_end(args); 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 5747c478bd9Sstevel@tonic-gate timespec_t rel; 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate rel.tv_sec = 0; 5777c478bd9Sstevel@tonic-gate rel.tv_nsec = BACKEND_PANIC_TIMEOUT; 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread != pthread_self()) { 5807c478bd9Sstevel@tonic-gate if (pthread_mutex_reltimedlock_np(&bes[i]->be_lock, 5817c478bd9Sstevel@tonic-gate &rel) != 0) 5827c478bd9Sstevel@tonic-gate failed++; 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate } 5857c478bd9Sstevel@tonic-gate if (failed) { 5867c478bd9Sstevel@tonic-gate configd_critical("unable to quiesce database\n"); 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate if (backend_panic_abort) 5907c478bd9Sstevel@tonic-gate abort(); 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate exit(CONFIGD_EXIT_DATABASE_BAD); 5937c478bd9Sstevel@tonic-gate } 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate /* 5967c478bd9Sstevel@tonic-gate * Returns 5977c478bd9Sstevel@tonic-gate * _SUCCESS 5987c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 5997c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory (_FULL & _TOOBIG?) 6007c478bd9Sstevel@tonic-gate */ 6017c478bd9Sstevel@tonic-gate static int 6027c478bd9Sstevel@tonic-gate backend_error(sqlite_backend_t *be, int error, char *errmsg) 6037c478bd9Sstevel@tonic-gate { 6047c478bd9Sstevel@tonic-gate if (error == SQLITE_OK) 6057c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate switch (error) { 6087c478bd9Sstevel@tonic-gate case SQLITE_ABORT: 6097c478bd9Sstevel@tonic-gate free(errmsg); 6107c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_DONE); 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate case SQLITE_NOMEM: 6137c478bd9Sstevel@tonic-gate case SQLITE_FULL: 6147c478bd9Sstevel@tonic-gate case SQLITE_TOOBIG: 6157c478bd9Sstevel@tonic-gate free(errmsg); 6167c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate default: 6197c478bd9Sstevel@tonic-gate backend_panic("%s: db error: %s", be->be_path, errmsg); 6207c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate static void 6257c478bd9Sstevel@tonic-gate backend_backup_cleanup(const char **out_arg, ssize_t out_sz) 6267c478bd9Sstevel@tonic-gate { 6277c478bd9Sstevel@tonic-gate char **out = (char **)out_arg; 6287c478bd9Sstevel@tonic-gate 6297c478bd9Sstevel@tonic-gate while (out_sz-- > 0) 6307c478bd9Sstevel@tonic-gate free(*out++); 6317c478bd9Sstevel@tonic-gate free(out_arg); 6327c478bd9Sstevel@tonic-gate } 6337c478bd9Sstevel@tonic-gate 6347c478bd9Sstevel@tonic-gate /* 6357c478bd9Sstevel@tonic-gate * builds a inverse-time-sorted array of backup files. The path is a 6367c478bd9Sstevel@tonic-gate * a single buffer, and the pointers look like: 6377c478bd9Sstevel@tonic-gate * 6387c478bd9Sstevel@tonic-gate * /this/is/a/full/path/to/repository-name-YYYYMMDDHHMMSS 6397c478bd9Sstevel@tonic-gate * ^pathname ^ ^(pathname+pathlen) 6407c478bd9Sstevel@tonic-gate * basename 6417c478bd9Sstevel@tonic-gate * 6427c478bd9Sstevel@tonic-gate * dirname will either be pathname, or ".". 6437c478bd9Sstevel@tonic-gate * 6447c478bd9Sstevel@tonic-gate * Returns the number of elements in the array, 0 if there are no previous 6457c478bd9Sstevel@tonic-gate * backups, or -1 on error. 6467c478bd9Sstevel@tonic-gate */ 6477c478bd9Sstevel@tonic-gate static ssize_t 6487c478bd9Sstevel@tonic-gate backend_backup_get_prev(char *pathname, size_t pathlen, const char ***out_arg) 6497c478bd9Sstevel@tonic-gate { 6507c478bd9Sstevel@tonic-gate char b_start, b_end; 6517c478bd9Sstevel@tonic-gate DIR *dir; 6527c478bd9Sstevel@tonic-gate char **out = NULL; 6537c478bd9Sstevel@tonic-gate char *name, *p; 6547c478bd9Sstevel@tonic-gate char *dirname, *basename; 6557c478bd9Sstevel@tonic-gate char *pathend; 6567c478bd9Sstevel@tonic-gate struct dirent *ent; 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate size_t count = 0; 6597c478bd9Sstevel@tonic-gate size_t baselen; 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate /* 6627c478bd9Sstevel@tonic-gate * year, month, day, hour, min, sec, plus an '_'. 6637c478bd9Sstevel@tonic-gate */ 6647c478bd9Sstevel@tonic-gate const size_t ndigits = 4 + 5*2 + 1; 6657c478bd9Sstevel@tonic-gate const size_t baroffset = 4 + 2*2; 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate size_t idx; 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate pathend = pathname + pathlen; 6707c478bd9Sstevel@tonic-gate b_end = *pathend; 6717c478bd9Sstevel@tonic-gate *pathend = '\0'; 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate basename = strrchr(pathname, '/'); 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate if (basename != NULL) { 6767c478bd9Sstevel@tonic-gate assert(pathend > pathname && basename < pathend); 6777c478bd9Sstevel@tonic-gate basename++; 6787c478bd9Sstevel@tonic-gate dirname = pathname; 6797c478bd9Sstevel@tonic-gate } else { 6807c478bd9Sstevel@tonic-gate basename = pathname; 6817c478bd9Sstevel@tonic-gate dirname = "."; 6827c478bd9Sstevel@tonic-gate } 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate baselen = strlen(basename); 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate /* 6877c478bd9Sstevel@tonic-gate * munge the string temporarily for the opendir(), then restore it. 6887c478bd9Sstevel@tonic-gate */ 6897c478bd9Sstevel@tonic-gate b_start = basename[0]; 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate basename[0] = '\0'; 6927c478bd9Sstevel@tonic-gate dir = opendir(dirname); 6937c478bd9Sstevel@tonic-gate basename[0] = b_start; /* restore path */ 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate if (dir == NULL) 6967c478bd9Sstevel@tonic-gate goto fail; 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate 6997c478bd9Sstevel@tonic-gate while ((ent = readdir(dir)) != NULL) { 7007c478bd9Sstevel@tonic-gate /* 7017c478bd9Sstevel@tonic-gate * Must match: 7027c478bd9Sstevel@tonic-gate * basename-YYYYMMDD_HHMMSS 7037c478bd9Sstevel@tonic-gate * or we ignore it. 7047c478bd9Sstevel@tonic-gate */ 7057c478bd9Sstevel@tonic-gate if (strncmp(ent->d_name, basename, baselen) != 0) 7067c478bd9Sstevel@tonic-gate continue; 7077c478bd9Sstevel@tonic-gate 7087c478bd9Sstevel@tonic-gate name = ent->d_name; 7097c478bd9Sstevel@tonic-gate if (name[baselen] != '-') 7107c478bd9Sstevel@tonic-gate continue; 7117c478bd9Sstevel@tonic-gate 7127c478bd9Sstevel@tonic-gate p = name + baselen + 1; 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate for (idx = 0; idx < ndigits; idx++) { 7157c478bd9Sstevel@tonic-gate char c = p[idx]; 7167c478bd9Sstevel@tonic-gate if (idx == baroffset && c != '_') 7177c478bd9Sstevel@tonic-gate break; 7187c478bd9Sstevel@tonic-gate if (idx != baroffset && (c < '0' || c > '9')) 7197c478bd9Sstevel@tonic-gate break; 7207c478bd9Sstevel@tonic-gate } 7217c478bd9Sstevel@tonic-gate if (idx != ndigits || p[idx] != '\0') 7227c478bd9Sstevel@tonic-gate continue; 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate /* 7257c478bd9Sstevel@tonic-gate * We have a match. insertion-sort it into our list. 7267c478bd9Sstevel@tonic-gate */ 7277c478bd9Sstevel@tonic-gate name = strdup(name); 7287c478bd9Sstevel@tonic-gate if (name == NULL) 7297c478bd9Sstevel@tonic-gate goto fail_closedir; 7307c478bd9Sstevel@tonic-gate p = strrchr(name, '-'); 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 7337c478bd9Sstevel@tonic-gate char *tmp = out[idx]; 7347c478bd9Sstevel@tonic-gate char *tp = strrchr(tmp, '-'); 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate int cmp = strcmp(p, tp); 7377c478bd9Sstevel@tonic-gate if (cmp == 0) 7387c478bd9Sstevel@tonic-gate cmp = strcmp(name, tmp); 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate if (cmp == 0) { 7417c478bd9Sstevel@tonic-gate free(name); 7427c478bd9Sstevel@tonic-gate name = NULL; 7437c478bd9Sstevel@tonic-gate break; 7447c478bd9Sstevel@tonic-gate } else if (cmp > 0) { 7457c478bd9Sstevel@tonic-gate out[idx] = name; 7467c478bd9Sstevel@tonic-gate name = tmp; 7477c478bd9Sstevel@tonic-gate p = tp; 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate } 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate if (idx == count) { 7527c478bd9Sstevel@tonic-gate char **new_out = realloc(out, 7537c478bd9Sstevel@tonic-gate (count + 1) * sizeof (*out)); 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate if (new_out == NULL) { 7567c478bd9Sstevel@tonic-gate free(name); 7577c478bd9Sstevel@tonic-gate goto fail_closedir; 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate 7607c478bd9Sstevel@tonic-gate out = new_out; 7617c478bd9Sstevel@tonic-gate out[count++] = name; 7627c478bd9Sstevel@tonic-gate } else { 7637c478bd9Sstevel@tonic-gate assert(name == NULL); 7647c478bd9Sstevel@tonic-gate } 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate (void) closedir(dir); 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate basename[baselen] = b_end; 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate *out_arg = (const char **)out; 7717c478bd9Sstevel@tonic-gate return (count); 7727c478bd9Sstevel@tonic-gate 7737c478bd9Sstevel@tonic-gate fail_closedir: 7747c478bd9Sstevel@tonic-gate (void) closedir(dir); 7757c478bd9Sstevel@tonic-gate fail: 7767c478bd9Sstevel@tonic-gate basename[0] = b_start; 7777c478bd9Sstevel@tonic-gate *pathend = b_end; 7787c478bd9Sstevel@tonic-gate 7797c478bd9Sstevel@tonic-gate backend_backup_cleanup((const char **)out, count); 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate *out_arg = NULL; 7827c478bd9Sstevel@tonic-gate return (-1); 7837c478bd9Sstevel@tonic-gate } 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate /* 7867c478bd9Sstevel@tonic-gate * Copies the repository path into out, a buffer of out_len bytes, 7877c478bd9Sstevel@tonic-gate * removes the ".db" (or whatever) extension, and, if name is non-NULL, 7887c478bd9Sstevel@tonic-gate * appends "-name" to it. If name is non-NULL, it can fail with: 7897c478bd9Sstevel@tonic-gate * 7907c478bd9Sstevel@tonic-gate * _TRUNCATED will not fit in buffer. 7917c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not a valid identifier 7927c478bd9Sstevel@tonic-gate */ 7937c478bd9Sstevel@tonic-gate static rep_protocol_responseid_t 7947c478bd9Sstevel@tonic-gate backend_backup_base(sqlite_backend_t *be, const char *name, 7957c478bd9Sstevel@tonic-gate char *out, size_t out_len) 7967c478bd9Sstevel@tonic-gate { 7977c478bd9Sstevel@tonic-gate char *p, *q; 7987c478bd9Sstevel@tonic-gate size_t len; 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate /* 8017c478bd9Sstevel@tonic-gate * for paths of the form /path/to/foo.db, we truncate at the final 8027c478bd9Sstevel@tonic-gate * '.'. 8037c478bd9Sstevel@tonic-gate */ 8049444c26fSTom Whitten (void) strlcpy(out, IS_VOLATILE(be) ? be->be_ppath : be->be_path, 8059444c26fSTom Whitten out_len); 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate p = strrchr(out, '/'); 8087c478bd9Sstevel@tonic-gate q = strrchr(out, '.'); 8097c478bd9Sstevel@tonic-gate 8107c478bd9Sstevel@tonic-gate if (p != NULL && q != NULL && q > p) 8117c478bd9Sstevel@tonic-gate *q = 0; 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate if (name != NULL) { 8147c478bd9Sstevel@tonic-gate len = strlen(out); 8157c478bd9Sstevel@tonic-gate assert(len < out_len); 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate out += len; 8187c478bd9Sstevel@tonic-gate out_len -= len; 8197c478bd9Sstevel@tonic-gate 8207c478bd9Sstevel@tonic-gate len = strlen(name); 8217c478bd9Sstevel@tonic-gate 8227c478bd9Sstevel@tonic-gate /* 8237c478bd9Sstevel@tonic-gate * verify that the name tag is entirely alphabetic, 8247c478bd9Sstevel@tonic-gate * non-empty, and not too long. 8257c478bd9Sstevel@tonic-gate */ 8267c478bd9Sstevel@tonic-gate if (len == 0 || len >= REP_PROTOCOL_NAME_LEN || 8277c478bd9Sstevel@tonic-gate uu_check_name(name, UU_NAME_DOMAIN) < 0) 8287c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 8297c478bd9Sstevel@tonic-gate 8307c478bd9Sstevel@tonic-gate if (snprintf(out, out_len, "-%s", name) >= out_len) 8317c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate 8377c478bd9Sstevel@tonic-gate /* 8389444c26fSTom Whitten * Make a checkpoint of the repository, so that we can use it for a backup 8399444c26fSTom Whitten * when the root file system becomes read/write. We'll first copy the 8409444c26fSTom Whitten * repository into a temporary file and then rename it to 8419444c26fSTom Whitten * REPOSITORY_CHECKPOINT. This is protection against configd crashing in 8429444c26fSTom Whitten * the middle of the copy and leaving a partial copy at 8439444c26fSTom Whitten * REPOSITORY_CHECKPOINT. Renames are atomic. 8449444c26fSTom Whitten */ 8459444c26fSTom Whitten static rep_protocol_responseid_t 8469444c26fSTom Whitten backend_checkpoint_repository(sqlite_backend_t *be) 8479444c26fSTom Whitten { 8489444c26fSTom Whitten rep_protocol_responseid_t r; 8499444c26fSTom Whitten 8509444c26fSTom Whitten assert(be->be_readonly); /* Only need a checkpoint if / is ro */ 8519444c26fSTom Whitten assert(be->be_type == BACKEND_TYPE_NORMAL); 8529444c26fSTom Whitten assert(be->be_checkpoint == NULL); /* Only 1 checkpoint */ 8539444c26fSTom Whitten 8549444c26fSTom Whitten r = backend_copy_repository(be->be_path, REPOSITORY_CHECKPOINT, 0); 8559444c26fSTom Whitten if (r == REP_PROTOCOL_SUCCESS) 8569444c26fSTom Whitten be->be_checkpoint = REPOSITORY_CHECKPOINT; 8579444c26fSTom Whitten 8589444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_CHECKPOINT, 8599444c26fSTom Whitten r == REP_PROTOCOL_SUCCESS ? BE_FLIGHT_ST_SUCCESS : 8609444c26fSTom Whitten BE_FLIGHT_ST_FAIL); 8619444c26fSTom Whitten 8629444c26fSTom Whitten return (r); 8639444c26fSTom Whitten } 8649444c26fSTom Whitten 8659444c26fSTom Whitten /* 8668918dff3Sjwadams * See if a backup is needed. We do a backup unless both files are 8678918dff3Sjwadams * byte-for-byte identical. 8688918dff3Sjwadams */ 8698918dff3Sjwadams static int 8708918dff3Sjwadams backend_check_backup_needed(const char *rep_name, const char *backup_name) 8718918dff3Sjwadams { 8728918dff3Sjwadams int repfd = open(rep_name, O_RDONLY); 8738918dff3Sjwadams int fd = open(backup_name, O_RDONLY); 8748918dff3Sjwadams struct stat s_rep, s_backup; 8758918dff3Sjwadams int c1, c2; 8768918dff3Sjwadams 8778918dff3Sjwadams FILE *f_rep = NULL; 8788918dff3Sjwadams FILE *f_backup = NULL; 8798918dff3Sjwadams 8808918dff3Sjwadams if (repfd < 0 || fd < 0) 8818918dff3Sjwadams goto fail; 8828918dff3Sjwadams 8838918dff3Sjwadams if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0) 8848918dff3Sjwadams goto fail; 8858918dff3Sjwadams 8868918dff3Sjwadams /* 8878918dff3Sjwadams * if they are the same file, we need to do a backup to break the 8888918dff3Sjwadams * hard link or symlink involved. 8898918dff3Sjwadams */ 8908918dff3Sjwadams if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev) 8918918dff3Sjwadams goto fail; 8928918dff3Sjwadams 8938918dff3Sjwadams if (s_rep.st_size != s_backup.st_size) 8948918dff3Sjwadams goto fail; 8958918dff3Sjwadams 8968918dff3Sjwadams if ((f_rep = fdopen(repfd, "r")) == NULL || 8978918dff3Sjwadams (f_backup = fdopen(fd, "r")) == NULL) 8988918dff3Sjwadams goto fail; 8998918dff3Sjwadams 9008918dff3Sjwadams do { 9018918dff3Sjwadams c1 = getc(f_rep); 9028918dff3Sjwadams c2 = getc(f_backup); 9038918dff3Sjwadams if (c1 != c2) 9048918dff3Sjwadams goto fail; 9058918dff3Sjwadams } while (c1 != EOF); 9068918dff3Sjwadams 9078918dff3Sjwadams if (!ferror(f_rep) && !ferror(f_backup)) { 9088918dff3Sjwadams (void) fclose(f_rep); 9098918dff3Sjwadams (void) fclose(f_backup); 9108918dff3Sjwadams (void) close(repfd); 9118918dff3Sjwadams (void) close(fd); 9128918dff3Sjwadams return (0); 9138918dff3Sjwadams } 9148918dff3Sjwadams 9158918dff3Sjwadams fail: 9168918dff3Sjwadams if (f_rep != NULL) 9178918dff3Sjwadams (void) fclose(f_rep); 9188918dff3Sjwadams if (f_backup != NULL) 9198918dff3Sjwadams (void) fclose(f_backup); 9208918dff3Sjwadams if (repfd >= 0) 9218918dff3Sjwadams (void) close(repfd); 9228918dff3Sjwadams if (fd >= 0) 9238918dff3Sjwadams (void) close(fd); 9248918dff3Sjwadams return (1); 9258918dff3Sjwadams } 9268918dff3Sjwadams 9278918dff3Sjwadams /* 928c0889d7aSstevep * This interface is called to perform the actual copy 929c0889d7aSstevep * 930c0889d7aSstevep * Return: 931c0889d7aSstevep * _FAIL_UNKNOWN read/write fails 932c0889d7aSstevep * _FAIL_NO_RESOURCES out of memory 933c0889d7aSstevep * _SUCCESS copy succeeds 934c0889d7aSstevep */ 935c0889d7aSstevep static rep_protocol_responseid_t 936c0889d7aSstevep backend_do_copy(const char *src, int srcfd, const char *dst, 937c0889d7aSstevep int dstfd, size_t *sz) 938c0889d7aSstevep { 939c0889d7aSstevep char *buf; 940c0889d7aSstevep off_t nrd, nwr, n, r_off = 0, w_off = 0; 941c0889d7aSstevep 942c0889d7aSstevep if ((buf = malloc(8192)) == NULL) 943c0889d7aSstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 944c0889d7aSstevep 945c0889d7aSstevep while ((nrd = read(srcfd, buf, 8192)) != 0) { 946c0889d7aSstevep if (nrd < 0) { 947c0889d7aSstevep if (errno == EINTR) 948c0889d7aSstevep continue; 949c0889d7aSstevep 950c0889d7aSstevep configd_critical( 951c0889d7aSstevep "Backend copy failed: fails to read from %s " 952c0889d7aSstevep "at offset %d: %s\n", src, r_off, strerror(errno)); 953c0889d7aSstevep free(buf); 954c0889d7aSstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 955c0889d7aSstevep } 956c0889d7aSstevep 957c0889d7aSstevep r_off += nrd; 958c0889d7aSstevep 959c0889d7aSstevep nwr = 0; 960c0889d7aSstevep do { 961c0889d7aSstevep if ((n = write(dstfd, &buf[nwr], nrd - nwr)) < 0) { 962c0889d7aSstevep if (errno == EINTR) 963c0889d7aSstevep continue; 964c0889d7aSstevep 965c0889d7aSstevep configd_critical( 966c0889d7aSstevep "Backend copy failed: fails to write to %s " 967c0889d7aSstevep "at offset %d: %s\n", dst, w_off, 968c0889d7aSstevep strerror(errno)); 969c0889d7aSstevep free(buf); 970c0889d7aSstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 971c0889d7aSstevep } 972c0889d7aSstevep 973c0889d7aSstevep nwr += n; 974c0889d7aSstevep w_off += n; 975c0889d7aSstevep 976c0889d7aSstevep } while (nwr < nrd); 977c0889d7aSstevep } 978c0889d7aSstevep 979c0889d7aSstevep if (sz) 980c0889d7aSstevep *sz = w_off; 981c0889d7aSstevep 982c0889d7aSstevep free(buf); 983c0889d7aSstevep return (REP_PROTOCOL_SUCCESS); 984c0889d7aSstevep } 985c0889d7aSstevep 986c0889d7aSstevep /* 9877c478bd9Sstevel@tonic-gate * Can return: 9887c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not valid 9897c478bd9Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 9907c478bd9Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 9917c478bd9Sstevel@tonic-gate * console) 9927c478bd9Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 993c0889d7aSstevep * _NO_RESOURCES out of memory 9947c478bd9Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 9957c478bd9Sstevel@tonic-gate */ 9967c478bd9Sstevel@tonic-gate static rep_protocol_responseid_t 9977c478bd9Sstevel@tonic-gate backend_create_backup_locked(sqlite_backend_t *be, const char *name) 9987c478bd9Sstevel@tonic-gate { 9997c478bd9Sstevel@tonic-gate const char **old_list; 10007c478bd9Sstevel@tonic-gate ssize_t old_sz; 10017c478bd9Sstevel@tonic-gate ssize_t old_max = max_repository_backups; 10027c478bd9Sstevel@tonic-gate ssize_t cur; 10037c478bd9Sstevel@tonic-gate char *finalname; 1004c0889d7aSstevep char *finalpath; 1005c0889d7aSstevep char *tmppath; 10067c478bd9Sstevel@tonic-gate int infd, outfd; 10077c478bd9Sstevel@tonic-gate size_t len; 10087c478bd9Sstevel@tonic-gate time_t now; 10097c478bd9Sstevel@tonic-gate struct tm now_tm; 10109444c26fSTom Whitten be_flight_status_t backup_type; 10117c478bd9Sstevel@tonic-gate rep_protocol_responseid_t result; 10129444c26fSTom Whitten const char *src; 10139444c26fSTom Whitten int use_checkpoint; 10149444c26fSTom Whitten 10159444c26fSTom Whitten if (strcmp(name, REPOSITORY_BOOT_BACKUP) == 0) { 10169444c26fSTom Whitten backup_type = BE_FLIGHT_ST_BOOT_BACKUP; 10179444c26fSTom Whitten } else if (strcmp(name, "manifest_import") == 0) { 10189444c26fSTom Whitten backup_type = BE_FLIGHT_ST_MI_BACKUP; 10199444c26fSTom Whitten } else { 10209444c26fSTom Whitten backup_type = BE_FLIGHT_ST_OTHER_BACKUP; 10219444c26fSTom Whitten } 10229444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_BACKUP_ENTER, backup_type); 10237c478bd9Sstevel@tonic-gate 1024c0889d7aSstevep if ((finalpath = malloc(PATH_MAX)) == NULL) 1025c0889d7aSstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 10267c478bd9Sstevel@tonic-gate 1027c0889d7aSstevep if ((tmppath = malloc(PATH_MAX)) == NULL) { 1028c0889d7aSstevep free(finalpath); 1029c0889d7aSstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1030c0889d7aSstevep } 1031c0889d7aSstevep 1032c0889d7aSstevep if (be->be_readonly) { 10339444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_NO_BACKUP, BE_FLIGHT_ST_RO); 1034c0889d7aSstevep result = REP_PROTOCOL_FAIL_BACKEND_READONLY; 1035c0889d7aSstevep goto out; 1036c0889d7aSstevep } 1037c0889d7aSstevep 1038c0889d7aSstevep result = backend_backup_base(be, name, finalpath, PATH_MAX); 10397c478bd9Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 1040c0889d7aSstevep goto out; 10417c478bd9Sstevel@tonic-gate 10429444c26fSTom Whitten /* 10439444c26fSTom Whitten * If this is a boot backup and if we made a checkpoint before the 10449444c26fSTom Whitten * root file system became read/write, then we should use the 10459444c26fSTom Whitten * checkpoint as the source. Otherwise, we'll use the actual 10469444c26fSTom Whitten * repository as the source. 10479444c26fSTom Whitten */ 10489444c26fSTom Whitten if (be->be_checkpoint && name && 10499444c26fSTom Whitten strcmp(REPOSITORY_BOOT_BACKUP, name) == 0) { 10509444c26fSTom Whitten backup_type = BE_FLIGHT_ST_CHECKPOINT_BACKUP; 10519444c26fSTom Whitten use_checkpoint = 1; 10529444c26fSTom Whitten src = be->be_checkpoint; 10539444c26fSTom Whitten } else { 10549444c26fSTom Whitten backup_type = BE_FLIGHT_ST_REPO_BACKUP; 10559444c26fSTom Whitten use_checkpoint = 0; 10569444c26fSTom Whitten src = be->be_path; 10579444c26fSTom Whitten } 10589444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_BACKUP, backup_type); 10599444c26fSTom Whitten if (!backend_check_backup_needed(src, finalpath)) { 10609444c26fSTom Whitten /* 10619444c26fSTom Whitten * No changes, so there is no need for a backup. 10629444c26fSTom Whitten */ 10639444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_NO_BACKUP, 10649444c26fSTom Whitten BE_FLIGHT_ST_DUPLICATE); 1065c0889d7aSstevep result = REP_PROTOCOL_SUCCESS; 1066c0889d7aSstevep goto out; 10678918dff3Sjwadams } 10688918dff3Sjwadams 10697c478bd9Sstevel@tonic-gate /* 10707c478bd9Sstevel@tonic-gate * remember the original length, and the basename location 10717c478bd9Sstevel@tonic-gate */ 10727c478bd9Sstevel@tonic-gate len = strlen(finalpath); 10737c478bd9Sstevel@tonic-gate finalname = strrchr(finalpath, '/'); 10747c478bd9Sstevel@tonic-gate if (finalname != NULL) 10757c478bd9Sstevel@tonic-gate finalname++; 10767c478bd9Sstevel@tonic-gate else 10777c478bd9Sstevel@tonic-gate finalname = finalpath; 10787c478bd9Sstevel@tonic-gate 1079c0889d7aSstevep (void) strlcpy(tmppath, finalpath, PATH_MAX); 1080c0889d7aSstevep if (strlcat(tmppath, "-tmpXXXXXX", PATH_MAX) >= PATH_MAX) { 1081c0889d7aSstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 1082c0889d7aSstevep goto out; 1083c0889d7aSstevep } 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate now = time(NULL); 10867c478bd9Sstevel@tonic-gate if (localtime_r(&now, &now_tm) == NULL) { 10877c478bd9Sstevel@tonic-gate configd_critical( 10887c478bd9Sstevel@tonic-gate "\"%s\" backup failed: localtime(3C) failed: %s\n", name, 10899444c26fSTom Whitten strerror(errno)); 1090c0889d7aSstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 1091c0889d7aSstevep goto out; 10927c478bd9Sstevel@tonic-gate } 10937c478bd9Sstevel@tonic-gate 1094c0889d7aSstevep if (strftime(finalpath + len, PATH_MAX - len, 1095c0889d7aSstevep "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= PATH_MAX - len) { 1096c0889d7aSstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 1097c0889d7aSstevep goto out; 10987c478bd9Sstevel@tonic-gate } 10997c478bd9Sstevel@tonic-gate 11009444c26fSTom Whitten infd = open(src, O_RDONLY); 11017c478bd9Sstevel@tonic-gate if (infd < 0) { 11027c478bd9Sstevel@tonic-gate configd_critical("\"%s\" backup failed: opening %s: %s\n", name, 11039444c26fSTom Whitten src, strerror(errno)); 1104c0889d7aSstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 1105c0889d7aSstevep goto out; 11067c478bd9Sstevel@tonic-gate } 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate outfd = mkstemp(tmppath); 11097c478bd9Sstevel@tonic-gate if (outfd < 0) { 11107c478bd9Sstevel@tonic-gate configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n", 11117c478bd9Sstevel@tonic-gate name, tmppath, strerror(errno)); 11127c478bd9Sstevel@tonic-gate (void) close(infd); 11137c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 1114c0889d7aSstevep goto out; 11157c478bd9Sstevel@tonic-gate } 11167c478bd9Sstevel@tonic-gate 11179444c26fSTom Whitten if ((result = backend_do_copy(src, infd, (const char *)tmppath, 11189444c26fSTom Whitten outfd, NULL)) != REP_PROTOCOL_SUCCESS) 11197c478bd9Sstevel@tonic-gate goto fail; 11207c478bd9Sstevel@tonic-gate 11217c478bd9Sstevel@tonic-gate /* 11227c478bd9Sstevel@tonic-gate * grab the old list before doing our re-name. 11237c478bd9Sstevel@tonic-gate */ 11247c478bd9Sstevel@tonic-gate if (old_max > 0) 11257c478bd9Sstevel@tonic-gate old_sz = backend_backup_get_prev(finalpath, len, &old_list); 11267c478bd9Sstevel@tonic-gate 11277c478bd9Sstevel@tonic-gate if (rename(tmppath, finalpath) < 0) { 11287c478bd9Sstevel@tonic-gate configd_critical( 11297c478bd9Sstevel@tonic-gate "\"%s\" backup failed: rename(%s, %s): %s\n", 11307c478bd9Sstevel@tonic-gate name, tmppath, finalpath, strerror(errno)); 11317c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 11327c478bd9Sstevel@tonic-gate goto fail; 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate tmppath[len] = 0; /* strip -XXXXXX, for reference symlink */ 11367c478bd9Sstevel@tonic-gate 11377c478bd9Sstevel@tonic-gate (void) unlink(tmppath); 11387c478bd9Sstevel@tonic-gate if (symlink(finalname, tmppath) < 0) { 11397c478bd9Sstevel@tonic-gate configd_critical( 11407c478bd9Sstevel@tonic-gate "\"%s\" backup completed, but updating " 11417c478bd9Sstevel@tonic-gate "\"%s\" symlink to \"%s\" failed: %s\n", 11427c478bd9Sstevel@tonic-gate name, tmppath, finalname, strerror(errno)); 11437c478bd9Sstevel@tonic-gate } 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate if (old_max > 0 && old_sz > 0) { 11467c478bd9Sstevel@tonic-gate /* unlink all but the first (old_max - 1) files */ 11477c478bd9Sstevel@tonic-gate for (cur = old_max - 1; cur < old_sz; cur++) { 11487c478bd9Sstevel@tonic-gate (void) strlcpy(finalname, old_list[cur], 1149c0889d7aSstevep PATH_MAX - (finalname - finalpath)); 11507c478bd9Sstevel@tonic-gate if (unlink(finalpath) < 0) 11517c478bd9Sstevel@tonic-gate configd_critical( 11527c478bd9Sstevel@tonic-gate "\"%s\" backup completed, but removing old " 11537c478bd9Sstevel@tonic-gate "file \"%s\" failed: %s\n", 11547c478bd9Sstevel@tonic-gate name, finalpath, strerror(errno)); 11557c478bd9Sstevel@tonic-gate } 11567c478bd9Sstevel@tonic-gate 11577c478bd9Sstevel@tonic-gate backend_backup_cleanup(old_list, old_sz); 11587c478bd9Sstevel@tonic-gate } 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_SUCCESS; 11619444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_BACKUP, BE_FLIGHT_ST_SUCCESS); 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate fail: 11647c478bd9Sstevel@tonic-gate (void) close(infd); 11657c478bd9Sstevel@tonic-gate (void) close(outfd); 11669444c26fSTom Whitten if (result != REP_PROTOCOL_SUCCESS) { 11679444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_BACKUP, BE_FLIGHT_ST_FAIL); 11687c478bd9Sstevel@tonic-gate (void) unlink(tmppath); 11699444c26fSTom Whitten } 11707c478bd9Sstevel@tonic-gate 1171c0889d7aSstevep out: 11729444c26fSTom Whitten /* Get rid of the checkpoint file now that we've used it. */ 11739444c26fSTom Whitten if (use_checkpoint && (result == REP_PROTOCOL_SUCCESS)) { 11749444c26fSTom Whitten (void) unlink(be->be_checkpoint); 11759444c26fSTom Whitten be->be_checkpoint = NULL; 11769444c26fSTom Whitten } 1177c0889d7aSstevep free(finalpath); 1178c0889d7aSstevep free(tmppath); 1179c0889d7aSstevep 11807c478bd9Sstevel@tonic-gate return (result); 11817c478bd9Sstevel@tonic-gate } 11827c478bd9Sstevel@tonic-gate 11836e1d2b42Samaguire /* 11846e1d2b42Samaguire * Check if value_tbl has been upgraded in the main database, and 11856e1d2b42Samaguire * if not (if the value_order column is not present), and do_upgrade is true, 11866e1d2b42Samaguire * upgrade value_tbl in repository to contain the additional value_order 11876e1d2b42Samaguire * column. The version of sqlite used means ALTER TABLE is not 11886e1d2b42Samaguire * available, so we cannot simply use "ALTER TABLE value_tbl ADD COLUMN". 11896e1d2b42Samaguire * Rather we need to create a temporary table with the additional column, 11906e1d2b42Samaguire * import the value_tbl, drop the original value_tbl, recreate the value_tbl 11916e1d2b42Samaguire * with the additional column, import the values from value_tbl_tmp, 11926e1d2b42Samaguire * reindex and finally drop value_tbl_tmp. During boot, we wish to check 11936e1d2b42Samaguire * if the repository has been upgraded before it is writable, so that 11946e1d2b42Samaguire * property value retrieval can use the appropriate form of the SELECT 11956e1d2b42Samaguire * statement that retrieves property values. As a result, we need to check 11966e1d2b42Samaguire * if the repository has been upgraded prior to the point when we can 11976e1d2b42Samaguire * actually carry out the update. 11986e1d2b42Samaguire */ 11996e1d2b42Samaguire void 12006e1d2b42Samaguire backend_check_upgrade(sqlite_backend_t *be, boolean_t do_upgrade) 12016e1d2b42Samaguire { 12026e1d2b42Samaguire char *errp; 12036e1d2b42Samaguire int r; 12046e1d2b42Samaguire 12056e1d2b42Samaguire if (be_normal_upgraded) 12066e1d2b42Samaguire return; 12076e1d2b42Samaguire /* 12086e1d2b42Samaguire * Test if upgrade is needed. If value_order column does not exist, 12096e1d2b42Samaguire * we need to upgrade the schema. 12106e1d2b42Samaguire */ 12116e1d2b42Samaguire r = sqlite_exec(be->be_db, "SELECT value_order FROM value_tbl LIMIT 1;", 12126e1d2b42Samaguire NULL, NULL, NULL); 12136e1d2b42Samaguire if (r == SQLITE_ERROR && do_upgrade) { 12146e1d2b42Samaguire /* No value_order column - needs upgrade */ 12156e1d2b42Samaguire configd_info("Upgrading SMF repository format..."); 12166e1d2b42Samaguire r = sqlite_exec(be->be_db, 12176e1d2b42Samaguire "BEGIN TRANSACTION; " 12186e1d2b42Samaguire "CREATE TABLE value_tbl_tmp ( " 12196e1d2b42Samaguire "value_id INTEGER NOT NULL, " 12206e1d2b42Samaguire "value_type CHAR(1) NOT NULL, " 12216e1d2b42Samaguire "value_value VARCHAR NOT NULL, " 12226e1d2b42Samaguire "value_order INTEGER DEFAULT 0); " 12236e1d2b42Samaguire "INSERT INTO value_tbl_tmp " 12246e1d2b42Samaguire "(value_id, value_type, value_value) " 12256e1d2b42Samaguire "SELECT value_id, value_type, value_value FROM value_tbl; " 12266e1d2b42Samaguire "DROP TABLE value_tbl; " 12276e1d2b42Samaguire "CREATE TABLE value_tbl( " 12286e1d2b42Samaguire "value_id INTEGER NOT NULL, " 12296e1d2b42Samaguire "value_type CHAR(1) NOT NULL, " 12306e1d2b42Samaguire "value_value VARCHAR NOT NULL, " 12316e1d2b42Samaguire "value_order INTEGER DEFAULT 0); " 12326e1d2b42Samaguire "INSERT INTO value_tbl SELECT * FROM value_tbl_tmp; " 12336e1d2b42Samaguire "CREATE INDEX value_tbl_id ON value_tbl (value_id); " 12346e1d2b42Samaguire "DROP TABLE value_tbl_tmp; " 12356e1d2b42Samaguire "COMMIT TRANSACTION; " 12366e1d2b42Samaguire "VACUUM; ", 12376e1d2b42Samaguire NULL, NULL, &errp); 12386e1d2b42Samaguire if (r == SQLITE_OK) { 12396e1d2b42Samaguire configd_info("SMF repository upgrade is complete."); 12406e1d2b42Samaguire } else { 12416e1d2b42Samaguire backend_panic("%s: repository upgrade failed: %s", 12426e1d2b42Samaguire be->be_path, errp); 12436e1d2b42Samaguire /* NOTREACHED */ 12446e1d2b42Samaguire } 12456e1d2b42Samaguire } 12466e1d2b42Samaguire if (r == SQLITE_OK) 12476e1d2b42Samaguire be_normal_upgraded = B_TRUE; 12486e1d2b42Samaguire else 12496e1d2b42Samaguire be_normal_upgraded = B_FALSE; 12506e1d2b42Samaguire } 12516e1d2b42Samaguire 12528918dff3Sjwadams static int 12538918dff3Sjwadams backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) 12548918dff3Sjwadams { 12559444c26fSTom Whitten const char *check_path; 12568918dff3Sjwadams char *errp; 12578918dff3Sjwadams struct sqlite *new; 12588918dff3Sjwadams int r; 12598918dff3Sjwadams 12608918dff3Sjwadams assert(be->be_readonly); 12618918dff3Sjwadams assert(be == bes[BACKEND_TYPE_NORMAL]); 12628918dff3Sjwadams 12638918dff3Sjwadams /* 12648918dff3Sjwadams * If we don't *need* to be writable, only check every once in a 12658918dff3Sjwadams * while. 12668918dff3Sjwadams */ 12678918dff3Sjwadams if (!writing) { 12688918dff3Sjwadams if ((uint64_t)(t - be->be_lastcheck) < 12698918dff3Sjwadams BACKEND_READONLY_CHECK_INTERVAL) 12708918dff3Sjwadams return (REP_PROTOCOL_SUCCESS); 12718918dff3Sjwadams be->be_lastcheck = t; 12728918dff3Sjwadams } 12738918dff3Sjwadams 12749444c26fSTom Whitten /* 12759444c26fSTom Whitten * It could be that the repository has been moved to non-persistent 12769444c26fSTom Whitten * storage for performance reasons. In this case we need to check 12779444c26fSTom Whitten * the persistent path to see if it is writable. The 12789444c26fSTom Whitten * non-persistent path will always be writable. 12799444c26fSTom Whitten */ 12809444c26fSTom Whitten check_path = IS_VOLATILE(be) ? be->be_ppath : be->be_path; 12819444c26fSTom Whitten 12829444c26fSTom Whitten new = sqlite_open(check_path, 0600, &errp); 12838918dff3Sjwadams if (new == NULL) { 12849444c26fSTom Whitten backend_panic("reopening %s: %s\n", check_path, errp); 12858918dff3Sjwadams /*NOTREACHED*/ 12868918dff3Sjwadams } 12879444c26fSTom Whitten r = backend_is_readonly(new, check_path); 12888918dff3Sjwadams 12898918dff3Sjwadams if (r != SQLITE_OK) { 12909444c26fSTom Whitten /* 12919444c26fSTom Whitten * The underlying storage for the permanent repository is 12929444c26fSTom Whitten * still read-only, so we don't want to change the state or 12939444c26fSTom Whitten * move the checkpointed backup if it exists. On the other 12949444c26fSTom Whitten * hand if the repository has been copied to volatile 12959444c26fSTom Whitten * storage, we'll let our caller go ahead and write to the 12969444c26fSTom Whitten * database. 12979444c26fSTom Whitten */ 12988918dff3Sjwadams sqlite_close(new); 12999444c26fSTom Whitten if (writing && (IS_VOLATILE(be) == 0)) 13008918dff3Sjwadams return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 13018918dff3Sjwadams return (REP_PROTOCOL_SUCCESS); 13028918dff3Sjwadams } 13038918dff3Sjwadams 13048918dff3Sjwadams /* 13059444c26fSTom Whitten * We can write! If the repository is not on volatile storage, 13069444c26fSTom Whitten * swap the db handles. Mark ourself as writable, upgrade the 13079444c26fSTom Whitten * repository if necessary and make a backup. 13088918dff3Sjwadams */ 13099444c26fSTom Whitten be->be_readonly = 0; 13109444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_TRANS_RW, BE_FLIGHT_ST_RW); 13119444c26fSTom Whitten if (IS_VOLATILE(be)) { 13129444c26fSTom Whitten /* 13139444c26fSTom Whitten * If the repository is on volatile storage, don't switch 13149444c26fSTom Whitten * the handles. We'll continue to use the repository that 13159444c26fSTom Whitten * is on tmpfs until we're told to move it back by one of 13169444c26fSTom Whitten * our clients. Clients, specifically manifest_import, 13179444c26fSTom Whitten * move the repository to tmpfs for performance reasons, 13189444c26fSTom Whitten * and that is the reason to not switch it back until we're 13199444c26fSTom Whitten * told to do so. 13209444c26fSTom Whitten */ 13219444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_TRANS_RW, 13229444c26fSTom Whitten BE_FLIGHT_ST_NO_SWITCH); 13239444c26fSTom Whitten sqlite_close(new); 13249444c26fSTom Whitten } else { 13259444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_TRANS_RW, 13269444c26fSTom Whitten BE_FLIGHT_ST_SWITCH); 13278918dff3Sjwadams sqlite_close(be->be_db); 13288918dff3Sjwadams be->be_db = new; 13299444c26fSTom Whitten } 13308918dff3Sjwadams 13316e1d2b42Samaguire if (be->be_type == BACKEND_TYPE_NORMAL) 13326e1d2b42Samaguire backend_check_upgrade(be, B_TRUE); 13336e1d2b42Samaguire 13348918dff3Sjwadams if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != 13358918dff3Sjwadams REP_PROTOCOL_SUCCESS) { 13368918dff3Sjwadams configd_critical( 13378918dff3Sjwadams "unable to create \"%s\" backup of \"%s\"\n", 13388918dff3Sjwadams REPOSITORY_BOOT_BACKUP, be->be_path); 13398918dff3Sjwadams } 13408918dff3Sjwadams 13418918dff3Sjwadams return (REP_PROTOCOL_SUCCESS); 13428918dff3Sjwadams } 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate /* 13457c478bd9Sstevel@tonic-gate * If t is not BACKEND_TYPE_NORMAL, can fail with 13467c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 13477c478bd9Sstevel@tonic-gate * 13487c478bd9Sstevel@tonic-gate * If writing is nonzero, can also fail with 13497c478bd9Sstevel@tonic-gate * _BACKEND_READONLY - backend is read-only 13507c478bd9Sstevel@tonic-gate */ 13517c478bd9Sstevel@tonic-gate static int 13527c478bd9Sstevel@tonic-gate backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) 13537c478bd9Sstevel@tonic-gate { 13547c478bd9Sstevel@tonic-gate sqlite_backend_t *be = NULL; 13557c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 13567c478bd9Sstevel@tonic-gate 13577c478bd9Sstevel@tonic-gate *bep = NULL; 13587c478bd9Sstevel@tonic-gate 13597c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || 13607c478bd9Sstevel@tonic-gate t == BACKEND_TYPE_NONPERSIST); 13617c478bd9Sstevel@tonic-gate 13627c478bd9Sstevel@tonic-gate be = bes[t]; 13637c478bd9Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) 13647c478bd9Sstevel@tonic-gate assert(be != NULL); /* should always be there */ 13657c478bd9Sstevel@tonic-gate 13667c478bd9Sstevel@tonic-gate if (be == NULL) 13677c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_ACCESS); 13687c478bd9Sstevel@tonic-gate 13697c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) 13707c478bd9Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 13717c478bd9Sstevel@tonic-gate 13727c478bd9Sstevel@tonic-gate ts = gethrtime(); 13737c478bd9Sstevel@tonic-gate vts = gethrvtime(); 13747c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 13757c478bd9Sstevel@tonic-gate UPDATE_TOTALS_WR(be, writing, bt_lock, ts, vts); 13767c478bd9Sstevel@tonic-gate 13777c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) { 13787c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 13797c478bd9Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate be->be_thread = pthread_self(); 13827c478bd9Sstevel@tonic-gate 13838918dff3Sjwadams if (be->be_readonly) { 13847c478bd9Sstevel@tonic-gate int r; 13857c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL); 13867c478bd9Sstevel@tonic-gate 13878918dff3Sjwadams r = backend_check_readonly(be, writing, ts); 13888918dff3Sjwadams if (r != REP_PROTOCOL_SUCCESS) { 13898918dff3Sjwadams be->be_thread = 0; 13908918dff3Sjwadams (void) pthread_mutex_unlock(&be->be_lock); 13918918dff3Sjwadams return (r); 13927c478bd9Sstevel@tonic-gate } 13938918dff3Sjwadams } 13948918dff3Sjwadams 13957c478bd9Sstevel@tonic-gate if (backend_do_trace) 13967c478bd9Sstevel@tonic-gate (void) sqlite_trace(be->be_db, backend_trace_sql, be); 13977c478bd9Sstevel@tonic-gate else 13987c478bd9Sstevel@tonic-gate (void) sqlite_trace(be->be_db, NULL, NULL); 13997c478bd9Sstevel@tonic-gate 14007c478bd9Sstevel@tonic-gate be->be_writing = writing; 14017c478bd9Sstevel@tonic-gate *bep = be; 14027c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 14037c478bd9Sstevel@tonic-gate } 14047c478bd9Sstevel@tonic-gate 14057c478bd9Sstevel@tonic-gate static void 14067c478bd9Sstevel@tonic-gate backend_unlock(sqlite_backend_t *be) 14077c478bd9Sstevel@tonic-gate { 14087c478bd9Sstevel@tonic-gate be->be_writing = 0; 14097c478bd9Sstevel@tonic-gate be->be_thread = 0; 14107c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 14117c478bd9Sstevel@tonic-gate } 14127c478bd9Sstevel@tonic-gate 14137c478bd9Sstevel@tonic-gate static void 14147c478bd9Sstevel@tonic-gate backend_destroy(sqlite_backend_t *be) 14157c478bd9Sstevel@tonic-gate { 14167c478bd9Sstevel@tonic-gate if (be->be_db != NULL) { 14177c478bd9Sstevel@tonic-gate sqlite_close(be->be_db); 14187c478bd9Sstevel@tonic-gate be->be_db = NULL; 14197c478bd9Sstevel@tonic-gate } 14207c478bd9Sstevel@tonic-gate be->be_thread = 0; 14217c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 14227c478bd9Sstevel@tonic-gate (void) pthread_mutex_destroy(&be->be_lock); 14237c478bd9Sstevel@tonic-gate } 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate static void 14267c478bd9Sstevel@tonic-gate backend_create_finish(backend_type_t backend_id, sqlite_backend_t *be) 14277c478bd9Sstevel@tonic-gate { 14287c478bd9Sstevel@tonic-gate assert(MUTEX_HELD(&be->be_lock)); 14297c478bd9Sstevel@tonic-gate assert(be == &be_info[backend_id]); 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate bes[backend_id] = be; 14327c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 14337c478bd9Sstevel@tonic-gate } 14347c478bd9Sstevel@tonic-gate 14357c478bd9Sstevel@tonic-gate static int 14367c478bd9Sstevel@tonic-gate backend_fd_write(int fd, const char *mess) 14377c478bd9Sstevel@tonic-gate { 14387c478bd9Sstevel@tonic-gate int len = strlen(mess); 14397c478bd9Sstevel@tonic-gate int written; 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate while (len > 0) { 14427c478bd9Sstevel@tonic-gate if ((written = write(fd, mess, len)) < 0) 14437c478bd9Sstevel@tonic-gate return (-1); 14447c478bd9Sstevel@tonic-gate mess += written; 14457c478bd9Sstevel@tonic-gate len -= written; 14467c478bd9Sstevel@tonic-gate } 14477c478bd9Sstevel@tonic-gate return (0); 14487c478bd9Sstevel@tonic-gate } 14497c478bd9Sstevel@tonic-gate 14507c478bd9Sstevel@tonic-gate /* 14517c478bd9Sstevel@tonic-gate * Can return: 14527c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not valid 14537c478bd9Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 14547c478bd9Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 14557c478bd9Sstevel@tonic-gate * console) 14567c478bd9Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 1457c0889d7aSstevep * _NO_RESOURCES out of memory 14587c478bd9Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 14597c478bd9Sstevel@tonic-gate */ 14607c478bd9Sstevel@tonic-gate rep_protocol_responseid_t 14617c478bd9Sstevel@tonic-gate backend_create_backup(const char *name) 14627c478bd9Sstevel@tonic-gate { 14637c478bd9Sstevel@tonic-gate rep_protocol_responseid_t result; 14647c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 14657c478bd9Sstevel@tonic-gate 14669444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_BACKUP, BE_FLIGHT_ST_CLIENT); 14677c478bd9Sstevel@tonic-gate result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be); 1468c0889d7aSstevep assert(result == REP_PROTOCOL_SUCCESS); 14697c478bd9Sstevel@tonic-gate 14707c478bd9Sstevel@tonic-gate result = backend_create_backup_locked(be, name); 14717c478bd9Sstevel@tonic-gate backend_unlock(be); 14727c478bd9Sstevel@tonic-gate 14737c478bd9Sstevel@tonic-gate return (result); 14747c478bd9Sstevel@tonic-gate } 14757c478bd9Sstevel@tonic-gate 1476c0889d7aSstevep /* 14779444c26fSTom Whitten * This function makes a copy of the repository at src, placing the copy at 14789444c26fSTom Whitten * dst. It is used to copy a repository on permanent storage to volatile 14799444c26fSTom Whitten * storage or vice versa. If the source file is on volatile storage, it is 14809444c26fSTom Whitten * often times desirable to delete it after the copy has been made and 14819444c26fSTom Whitten * verified. To remove the source repository, set remove_src to 1. 1482c0889d7aSstevep * 1483c0889d7aSstevep * Can return: 1484c0889d7aSstevep * 1485c0889d7aSstevep * REP_PROTOCOL_SUCCESS successful copy and rename 1486c0889d7aSstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 1487c0889d7aSstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 1488c0889d7aSstevep */ 1489c0889d7aSstevep static rep_protocol_responseid_t 14909444c26fSTom Whitten backend_copy_repository(const char *src, const char *dst, int remove_src) 1491c0889d7aSstevep { 1492c0889d7aSstevep int srcfd, dstfd; 1493c0889d7aSstevep char *tmppath = malloc(PATH_MAX); 1494c0889d7aSstevep rep_protocol_responseid_t res = REP_PROTOCOL_SUCCESS; 1495c0889d7aSstevep struct stat s_buf; 1496c0889d7aSstevep size_t cpsz, sz; 1497c0889d7aSstevep 1498c0889d7aSstevep if (tmppath == NULL) { 1499c0889d7aSstevep res = REP_PROTOCOL_FAIL_NO_RESOURCES; 1500c0889d7aSstevep goto out; 1501c0889d7aSstevep } 1502c0889d7aSstevep 1503c0889d7aSstevep /* 1504c0889d7aSstevep * Create and open the related db files 1505c0889d7aSstevep */ 1506c0889d7aSstevep (void) strlcpy(tmppath, dst, PATH_MAX); 1507c0889d7aSstevep sz = strlcat(tmppath, "-XXXXXX", PATH_MAX); 1508c0889d7aSstevep assert(sz < PATH_MAX); 1509c0889d7aSstevep if (sz >= PATH_MAX) { 1510c0889d7aSstevep configd_critical( 1511c0889d7aSstevep "Backend copy failed: strlcat %s: overflow\n", tmppath); 1512c0889d7aSstevep abort(); 1513c0889d7aSstevep } 1514c0889d7aSstevep 1515c0889d7aSstevep if ((dstfd = mkstemp(tmppath)) < 0) { 1516c0889d7aSstevep configd_critical("Backend copy failed: mkstemp %s: %s\n", 1517c0889d7aSstevep tmppath, strerror(errno)); 1518c0889d7aSstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1519c0889d7aSstevep goto out; 1520c0889d7aSstevep } 1521c0889d7aSstevep 1522c0889d7aSstevep if ((srcfd = open(src, O_RDONLY)) < 0) { 1523c0889d7aSstevep configd_critical("Backend copy failed: opening %s: %s\n", 1524c0889d7aSstevep src, strerror(errno)); 1525c0889d7aSstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1526c0889d7aSstevep goto errexit; 1527c0889d7aSstevep } 1528c0889d7aSstevep 1529c0889d7aSstevep /* 1530c0889d7aSstevep * fstat the backend before copy for sanity check. 1531c0889d7aSstevep */ 1532c0889d7aSstevep if (fstat(srcfd, &s_buf) < 0) { 1533c0889d7aSstevep configd_critical("Backend copy failed: fstat %s: %s\n", 1534c0889d7aSstevep src, strerror(errno)); 1535c0889d7aSstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1536c0889d7aSstevep goto errexit; 1537c0889d7aSstevep } 1538c0889d7aSstevep 1539c0889d7aSstevep if ((res = backend_do_copy(src, srcfd, dst, dstfd, &cpsz)) != 1540c0889d7aSstevep REP_PROTOCOL_SUCCESS) 1541c0889d7aSstevep goto errexit; 1542c0889d7aSstevep 1543c0889d7aSstevep if (cpsz != s_buf.st_size) { 1544c0889d7aSstevep configd_critical("Backend copy failed: incomplete copy\n"); 1545c0889d7aSstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1546c0889d7aSstevep goto errexit; 1547c0889d7aSstevep } 1548c0889d7aSstevep 1549c0889d7aSstevep /* 1550c0889d7aSstevep * Rename tmppath to dst 1551c0889d7aSstevep */ 1552c0889d7aSstevep if (rename(tmppath, dst) < 0) { 1553c0889d7aSstevep configd_critical( 1554c0889d7aSstevep "Backend copy failed: rename %s to %s: %s\n", 1555c0889d7aSstevep tmppath, dst, strerror(errno)); 1556c0889d7aSstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1557c0889d7aSstevep } 1558c0889d7aSstevep 1559c0889d7aSstevep errexit: 1560c0889d7aSstevep if (res != REP_PROTOCOL_SUCCESS && unlink(tmppath) < 0) 1561c0889d7aSstevep configd_critical( 1562c0889d7aSstevep "Backend copy failed: remove %s: %s\n", 1563c0889d7aSstevep tmppath, strerror(errno)); 1564c0889d7aSstevep 1565c0889d7aSstevep (void) close(srcfd); 1566c0889d7aSstevep (void) close(dstfd); 1567c0889d7aSstevep 1568c0889d7aSstevep out: 1569c0889d7aSstevep free(tmppath); 15709444c26fSTom Whitten if (remove_src) { 1571c0889d7aSstevep if (unlink(src) < 0) 1572c0889d7aSstevep configd_critical( 1573c0889d7aSstevep "Backend copy failed: remove %s: %s\n", 1574c0889d7aSstevep src, strerror(errno)); 1575c0889d7aSstevep } 1576c0889d7aSstevep 1577c0889d7aSstevep return (res); 1578c0889d7aSstevep } 1579c0889d7aSstevep 1580c0889d7aSstevep /* 1581c0889d7aSstevep * Perform sanity check on the repository. 1582c0889d7aSstevep * Return 0 if check succeeds or -1 if fails. 1583c0889d7aSstevep */ 1584c0889d7aSstevep static int 1585c0889d7aSstevep backend_switch_check(struct sqlite *be_db, char **errp) 1586c0889d7aSstevep { 1587c0889d7aSstevep struct run_single_int_info info; 1588c0889d7aSstevep uint32_t val = -1UL; 1589c0889d7aSstevep int r; 1590c0889d7aSstevep 1591c0889d7aSstevep info.rs_out = &val; 1592c0889d7aSstevep info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 1593c0889d7aSstevep 1594c0889d7aSstevep r = sqlite_exec(be_db, 1595c0889d7aSstevep "SELECT schema_version FROM schema_version;", 1596c0889d7aSstevep run_single_int_callback, &info, errp); 1597c0889d7aSstevep 1598c0889d7aSstevep if (r == SQLITE_OK && 1599c0889d7aSstevep info.rs_result != REP_PROTOCOL_FAIL_NOT_FOUND && 1600c0889d7aSstevep val == BACKEND_SCHEMA_VERSION) 1601c0889d7aSstevep return (0); 1602c0889d7aSstevep else 1603c0889d7aSstevep return (-1); 1604c0889d7aSstevep } 1605c0889d7aSstevep 1606c0889d7aSstevep /* 16079444c26fSTom Whitten * backend_switch() implements the REP_PROTOCOL_SWITCH request from 16089444c26fSTom Whitten * clients. First, it blocks all other clients from accessing the 16099444c26fSTom Whitten * repository by calling backend_lock to lock the repository. It either 16109444c26fSTom Whitten * copies the repository from it's permanent storage location 16119444c26fSTom Whitten * (REPOSITORY_DB) to its fast volatile location (FAST_REPOSITORY_DB), or 16129444c26fSTom Whitten * vice versa. dir determines the direction of the copy. 16139444c26fSTom Whitten * 16149444c26fSTom Whitten * dir = 0 Copy from permanent location to volatile location. 16159444c26fSTom Whitten * dir = 1 Copy from volatile location to permanent location. 1616c0889d7aSstevep * 1617c0889d7aSstevep * Can return: 1618c0889d7aSstevep * REP_PROTOCOL_SUCCESS successful switch 1619c0889d7aSstevep * REP_PROTOCOL_FAIL_BACKEND_ACCESS backen access fails 1620c0889d7aSstevep * REP_PROTOCOL_FAIL_BACKEND_READONLY backend is not writable 1621c0889d7aSstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 1622c0889d7aSstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 1623c0889d7aSstevep */ 1624c0889d7aSstevep rep_protocol_responseid_t 16259444c26fSTom Whitten backend_switch(int dir) 1626c0889d7aSstevep { 1627c0889d7aSstevep rep_protocol_responseid_t result; 1628c0889d7aSstevep sqlite_backend_t *be; 1629c0889d7aSstevep struct sqlite *new; 1630c0889d7aSstevep char *errp; 1631c0889d7aSstevep const char *dst; 1632c0889d7aSstevep 16339444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, BE_FLIGHT_ST_CLIENT); 16349444c26fSTom Whitten 16359444c26fSTom Whitten /* 16369444c26fSTom Whitten * If switching back to the main repository, lock for writing. 16379444c26fSTom Whitten * Otherwise, lock for reading. 16389444c26fSTom Whitten */ 16399444c26fSTom Whitten result = backend_lock(BACKEND_TYPE_NORMAL, dir ? 1 : 0, 16409444c26fSTom Whitten &be); 1641c0889d7aSstevep if (result != REP_PROTOCOL_SUCCESS) 1642c0889d7aSstevep return (result); 1643c0889d7aSstevep 16449444c26fSTom Whitten if (dir) { 16459444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, 16469444c26fSTom Whitten BE_FLIGHT_ST_PERMANENT); 1647c0889d7aSstevep dst = REPOSITORY_DB; 1648c0889d7aSstevep } else { 16499444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, 16509444c26fSTom Whitten BE_FLIGHT_ST_FAST); 1651c0889d7aSstevep dst = FAST_REPOSITORY_DB; 1652c0889d7aSstevep } 1653c0889d7aSstevep 1654c0889d7aSstevep /* 1655c0889d7aSstevep * Do the actual copy and rename 1656c0889d7aSstevep */ 16579444c26fSTom Whitten if (strcmp(be->be_path, dst) == 0) { 16589444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, 16599444c26fSTom Whitten BE_FLIGHT_ST_DUPLICATE); 16609444c26fSTom Whitten result = REP_PROTOCOL_SUCCESS; 16619444c26fSTom Whitten goto errout; 16629444c26fSTom Whitten } 16639444c26fSTom Whitten 16649444c26fSTom Whitten result = backend_copy_repository(be->be_path, dst, dir); 1665c0889d7aSstevep if (result != REP_PROTOCOL_SUCCESS) { 1666c0889d7aSstevep goto errout; 1667c0889d7aSstevep } 1668c0889d7aSstevep 1669c0889d7aSstevep /* 1670c0889d7aSstevep * Do the backend sanity check and switch 1671c0889d7aSstevep */ 1672c0889d7aSstevep new = sqlite_open(dst, 0600, &errp); 1673c0889d7aSstevep if (new != NULL) { 1674c0889d7aSstevep /* 1675c0889d7aSstevep * Sanity check 1676c0889d7aSstevep */ 1677c0889d7aSstevep if (backend_switch_check(new, &errp) == 0) { 1678c0889d7aSstevep free((char *)be->be_path); 1679c0889d7aSstevep be->be_path = strdup(dst); 1680c0889d7aSstevep if (be->be_path == NULL) { 1681c0889d7aSstevep configd_critical( 1682c0889d7aSstevep "Backend switch failed: strdup %s: %s\n", 1683c0889d7aSstevep dst, strerror(errno)); 1684c0889d7aSstevep result = REP_PROTOCOL_FAIL_NO_RESOURCES; 1685c0889d7aSstevep sqlite_close(new); 1686c0889d7aSstevep } else { 1687c0889d7aSstevep sqlite_close(be->be_db); 1688c0889d7aSstevep be->be_db = new; 16899444c26fSTom Whitten if (dir) { 16909444c26fSTom Whitten /* We're back on permanent storage. */ 16919444c26fSTom Whitten be->be_ppath = NULL; 16929444c26fSTom Whitten } else { 16939444c26fSTom Whitten /* 16949444c26fSTom Whitten * Repository is now on volatile 16959444c26fSTom Whitten * storage. Save the location of 16969444c26fSTom Whitten * the persistent repository. 16979444c26fSTom Whitten */ 16989444c26fSTom Whitten be->be_ppath = REPOSITORY_DB; 16999444c26fSTom Whitten } 1700c0889d7aSstevep } 1701c0889d7aSstevep } else { 1702c0889d7aSstevep configd_critical( 1703c0889d7aSstevep "Backend switch failed: integrity check %s: %s\n", 1704c0889d7aSstevep dst, errp); 1705c0889d7aSstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 1706c0889d7aSstevep } 1707c0889d7aSstevep } else { 1708c0889d7aSstevep configd_critical("Backend switch failed: sqlite_open %s: %s\n", 1709c0889d7aSstevep dst, errp); 1710c0889d7aSstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 1711c0889d7aSstevep } 1712c0889d7aSstevep 1713c0889d7aSstevep errout: 17149444c26fSTom Whitten if (result == REP_PROTOCOL_SUCCESS) { 17159444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, 17169444c26fSTom Whitten BE_FLIGHT_ST_SUCCESS); 17179444c26fSTom Whitten } else { 17189444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_SWITCH, BE_FLIGHT_ST_FAIL); 17199444c26fSTom Whitten } 1720c0889d7aSstevep backend_unlock(be); 1721c0889d7aSstevep return (result); 1722c0889d7aSstevep } 1723c0889d7aSstevep 1724c0889d7aSstevep /* 1725c0889d7aSstevep * This routine is called to attempt the recovery of 1726c0889d7aSstevep * the most recent valid repository if possible when configd 1727c0889d7aSstevep * is restarted for some reasons or when system crashes 1728c0889d7aSstevep * during the switch operation. The repository databases 1729c0889d7aSstevep * referenced here are indicators of successful switch 1730c0889d7aSstevep * operations. 1731c0889d7aSstevep */ 17329444c26fSTom Whitten static backend_switch_results_t 1733c0889d7aSstevep backend_switch_recovery(void) 1734c0889d7aSstevep { 1735c0889d7aSstevep const char *fast_db = FAST_REPOSITORY_DB; 17369444c26fSTom Whitten char *errp = NULL; 1737c0889d7aSstevep struct stat s_buf; 1738c0889d7aSstevep struct sqlite *be_db; 17399444c26fSTom Whitten int r; 17409444c26fSTom Whitten backend_switch_results_t res = BACKEND_SWITCH_OK; 1741c0889d7aSstevep 1742c0889d7aSstevep /* 1743c0889d7aSstevep * A good transient db containing most recent data can 17449444c26fSTom Whitten * exist if svc.configd crashes during the 1745c0889d7aSstevep * switch operation. If that is the case, check its 1746c0889d7aSstevep * integrity and use it. 1747c0889d7aSstevep */ 1748c0889d7aSstevep if (stat(fast_db, &s_buf) < 0) { 17499444c26fSTom Whitten return (BACKEND_SWITCH_OK); 17509444c26fSTom Whitten } 17519444c26fSTom Whitten 17529444c26fSTom Whitten /* Determine if persistent repository is read-only */ 17539444c26fSTom Whitten be_db = sqlite_open(REPOSITORY_DB, 0600, &errp); 17549444c26fSTom Whitten if (be_db == NULL) { 17559444c26fSTom Whitten configd_critical("Unable to open \"%s\". %s\n", 17569444c26fSTom Whitten REPOSITORY_DB, errp == NULL ? "" : errp); 17579444c26fSTom Whitten free(errp); 17589444c26fSTom Whitten return (BACKEND_SWITCH_FATAL); 17599444c26fSTom Whitten } 17609444c26fSTom Whitten r = backend_is_readonly(be_db, REPOSITORY_DB); 17619444c26fSTom Whitten sqlite_close(be_db); 17629444c26fSTom Whitten if (r != SQLITE_OK) { 17639444c26fSTom Whitten if (r == SQLITE_READONLY) { 17649444c26fSTom Whitten return (BACKEND_SWITCH_RO); 17659444c26fSTom Whitten } 17669444c26fSTom Whitten return (BACKEND_SWITCH_FATAL); 1767c0889d7aSstevep } 1768c0889d7aSstevep 1769c0889d7aSstevep /* 1770c0889d7aSstevep * Do sanity check on the db 1771c0889d7aSstevep */ 1772c0889d7aSstevep be_db = sqlite_open(fast_db, 0600, &errp); 1773c0889d7aSstevep 1774c0889d7aSstevep if (be_db != NULL) { 17759444c26fSTom Whitten if (backend_switch_check(be_db, &errp) == 0) { 17769444c26fSTom Whitten if (backend_copy_repository(fast_db, 17779444c26fSTom Whitten REPOSITORY_DB, 1) != REP_PROTOCOL_SUCCESS) { 17789444c26fSTom Whitten res = BACKEND_SWITCH_FATAL; 1779c0889d7aSstevep } 17809444c26fSTom Whitten } 17819444c26fSTom Whitten sqlite_close(be_db); 17829444c26fSTom Whitten } 17839444c26fSTom Whitten free(errp); 1784c0889d7aSstevep 17859444c26fSTom Whitten /* 17869444c26fSTom Whitten * If we get to this point, the fast_db has either been copied or 17879444c26fSTom Whitten * it is useless. Either way, get rid of it. 17889444c26fSTom Whitten */ 1789c0889d7aSstevep (void) unlink(fast_db); 17909444c26fSTom Whitten 17919444c26fSTom Whitten return (res); 1792c0889d7aSstevep } 1793c0889d7aSstevep 17947c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 17957c478bd9Sstevel@tonic-gate static int 17967c478bd9Sstevel@tonic-gate backend_integrity_callback(void *private, int narg, char **vals, char **cols) 17977c478bd9Sstevel@tonic-gate { 17987c478bd9Sstevel@tonic-gate char **out = private; 17997c478bd9Sstevel@tonic-gate char *old = *out; 18007c478bd9Sstevel@tonic-gate char *new; 18017c478bd9Sstevel@tonic-gate const char *info; 18027c478bd9Sstevel@tonic-gate size_t len; 18037c478bd9Sstevel@tonic-gate int x; 18047c478bd9Sstevel@tonic-gate 18057c478bd9Sstevel@tonic-gate for (x = 0; x < narg; x++) { 18067c478bd9Sstevel@tonic-gate if ((info = vals[x]) != NULL && 18077c478bd9Sstevel@tonic-gate strcmp(info, "ok") != 0) { 18087c478bd9Sstevel@tonic-gate len = (old == NULL)? 0 : strlen(old); 18097c478bd9Sstevel@tonic-gate len += strlen(info) + 2; /* '\n' + '\0' */ 18107c478bd9Sstevel@tonic-gate 18117c478bd9Sstevel@tonic-gate new = realloc(old, len); 18127c478bd9Sstevel@tonic-gate if (new == NULL) 18137c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 18147c478bd9Sstevel@tonic-gate if (old == NULL) 18157c478bd9Sstevel@tonic-gate new[0] = 0; 18167c478bd9Sstevel@tonic-gate old = *out = new; 18177c478bd9Sstevel@tonic-gate (void) strlcat(new, info, len); 18187c478bd9Sstevel@tonic-gate (void) strlcat(new, "\n", len); 18197c478bd9Sstevel@tonic-gate } 18207c478bd9Sstevel@tonic-gate } 18217c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 18227c478bd9Sstevel@tonic-gate } 18237c478bd9Sstevel@tonic-gate 18247c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_LOCKED -2 18257c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_FAIL -1 18267c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_SUCCESS 0 18277c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_READONLY 1 18287c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_NEED_INIT 2 18297c478bd9Sstevel@tonic-gate static int 18307c478bd9Sstevel@tonic-gate backend_create(backend_type_t backend_id, const char *db_file, 18317c478bd9Sstevel@tonic-gate sqlite_backend_t **bep) 18327c478bd9Sstevel@tonic-gate { 18337c478bd9Sstevel@tonic-gate char *errp; 18347c478bd9Sstevel@tonic-gate char *integrity_results = NULL; 18357c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 18367c478bd9Sstevel@tonic-gate int r; 18377c478bd9Sstevel@tonic-gate uint32_t val = -1UL; 18387c478bd9Sstevel@tonic-gate struct run_single_int_info info; 18397c478bd9Sstevel@tonic-gate int fd; 18407c478bd9Sstevel@tonic-gate 18417c478bd9Sstevel@tonic-gate assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); 18427c478bd9Sstevel@tonic-gate 18437c478bd9Sstevel@tonic-gate be = &be_info[backend_id]; 18446e1d2b42Samaguire 18457c478bd9Sstevel@tonic-gate assert(be->be_db == NULL); 18467c478bd9Sstevel@tonic-gate 18477c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&be->be_lock, NULL); 18487c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 18497c478bd9Sstevel@tonic-gate 18507c478bd9Sstevel@tonic-gate be->be_type = backend_id; 18517c478bd9Sstevel@tonic-gate be->be_path = strdup(db_file); 18527c478bd9Sstevel@tonic-gate if (be->be_path == NULL) { 18537c478bd9Sstevel@tonic-gate perror("malloc"); 18547c478bd9Sstevel@tonic-gate goto fail; 18557c478bd9Sstevel@tonic-gate } 18567c478bd9Sstevel@tonic-gate 18577c478bd9Sstevel@tonic-gate be->be_db = sqlite_open(be->be_path, 0600, &errp); 18587c478bd9Sstevel@tonic-gate 18597c478bd9Sstevel@tonic-gate if (be->be_db == NULL) { 18607c478bd9Sstevel@tonic-gate if (strstr(errp, "out of memory") != NULL) { 18617c478bd9Sstevel@tonic-gate configd_critical("%s: %s\n", db_file, errp); 18627c478bd9Sstevel@tonic-gate free(errp); 18637c478bd9Sstevel@tonic-gate 18647c478bd9Sstevel@tonic-gate goto fail; 18657c478bd9Sstevel@tonic-gate } 18667c478bd9Sstevel@tonic-gate 18677c478bd9Sstevel@tonic-gate /* report it as an integrity failure */ 18687c478bd9Sstevel@tonic-gate integrity_results = errp; 18697c478bd9Sstevel@tonic-gate errp = NULL; 18707c478bd9Sstevel@tonic-gate goto integrity_fail; 18717c478bd9Sstevel@tonic-gate } 18727c478bd9Sstevel@tonic-gate 18737c478bd9Sstevel@tonic-gate /* 18747c478bd9Sstevel@tonic-gate * check if we are inited and of the correct schema version 18757c478bd9Sstevel@tonic-gate * 18767c478bd9Sstevel@tonic-gate */ 18777c478bd9Sstevel@tonic-gate info.rs_out = &val; 18787c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 18797c478bd9Sstevel@tonic-gate 18807c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT schema_version FROM schema_version;", 18817c478bd9Sstevel@tonic-gate run_single_int_callback, &info, &errp); 18827c478bd9Sstevel@tonic-gate if (r == SQLITE_ERROR && 18837c478bd9Sstevel@tonic-gate strcmp("no such table: schema_version", errp) == 0) { 18847c478bd9Sstevel@tonic-gate free(errp); 18857c478bd9Sstevel@tonic-gate /* 18867c478bd9Sstevel@tonic-gate * Could be an empty repository, could be pre-schema_version 18877c478bd9Sstevel@tonic-gate * schema. Check for id_tbl, which has always been there. 18887c478bd9Sstevel@tonic-gate */ 18897c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT count() FROM id_tbl;", 18907c478bd9Sstevel@tonic-gate NULL, NULL, &errp); 18917c478bd9Sstevel@tonic-gate if (r == SQLITE_ERROR && 18927c478bd9Sstevel@tonic-gate strcmp("no such table: id_tbl", errp) == 0) { 18937c478bd9Sstevel@tonic-gate free(errp); 18947c478bd9Sstevel@tonic-gate *bep = be; 18957c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_NEED_INIT); 18967c478bd9Sstevel@tonic-gate } 18977c478bd9Sstevel@tonic-gate 18987c478bd9Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", db_file); 18997c478bd9Sstevel@tonic-gate goto fail; 19007c478bd9Sstevel@tonic-gate } 19017c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 19027c478bd9Sstevel@tonic-gate free(errp); 19037c478bd9Sstevel@tonic-gate *bep = NULL; 19047c478bd9Sstevel@tonic-gate backend_destroy(be); 19057c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 19067c478bd9Sstevel@tonic-gate } 19077c478bd9Sstevel@tonic-gate if (r == SQLITE_OK) { 19087c478bd9Sstevel@tonic-gate if (info.rs_result == REP_PROTOCOL_FAIL_NOT_FOUND || 19097c478bd9Sstevel@tonic-gate val != BACKEND_SCHEMA_VERSION) { 19107c478bd9Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", 19117c478bd9Sstevel@tonic-gate db_file); 19127c478bd9Sstevel@tonic-gate goto fail; 19137c478bd9Sstevel@tonic-gate } 19147c478bd9Sstevel@tonic-gate } 19157c478bd9Sstevel@tonic-gate 19167c478bd9Sstevel@tonic-gate /* 19177c478bd9Sstevel@tonic-gate * pull in the whole database sequentially. 19187c478bd9Sstevel@tonic-gate */ 19197c478bd9Sstevel@tonic-gate if ((fd = open(db_file, O_RDONLY)) >= 0) { 19207c478bd9Sstevel@tonic-gate size_t sz = 64 * 1024; 19217c478bd9Sstevel@tonic-gate char *buffer = malloc(sz); 19227c478bd9Sstevel@tonic-gate if (buffer != NULL) { 19237c478bd9Sstevel@tonic-gate while (read(fd, buffer, sz) > 0) 19247c478bd9Sstevel@tonic-gate ; 19257c478bd9Sstevel@tonic-gate free(buffer); 19267c478bd9Sstevel@tonic-gate } 19277c478bd9Sstevel@tonic-gate (void) close(fd); 19287c478bd9Sstevel@tonic-gate } 19297c478bd9Sstevel@tonic-gate 19307c478bd9Sstevel@tonic-gate /* 19317c478bd9Sstevel@tonic-gate * run an integrity check 19327c478bd9Sstevel@tonic-gate */ 19337c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "PRAGMA integrity_check;", 19347c478bd9Sstevel@tonic-gate backend_integrity_callback, &integrity_results, &errp); 19357c478bd9Sstevel@tonic-gate 19367c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 19377c478bd9Sstevel@tonic-gate free(errp); 19387c478bd9Sstevel@tonic-gate *bep = NULL; 19397c478bd9Sstevel@tonic-gate backend_destroy(be); 19407c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 19417c478bd9Sstevel@tonic-gate } 19427c478bd9Sstevel@tonic-gate if (r == SQLITE_ABORT) { 19437c478bd9Sstevel@tonic-gate free(errp); 19447c478bd9Sstevel@tonic-gate errp = NULL; 19457c478bd9Sstevel@tonic-gate integrity_results = "out of memory running integrity check\n"; 19467c478bd9Sstevel@tonic-gate } else if (r != SQLITE_OK && integrity_results == NULL) { 19477c478bd9Sstevel@tonic-gate integrity_results = errp; 19487c478bd9Sstevel@tonic-gate errp = NULL; 19497c478bd9Sstevel@tonic-gate } 19507c478bd9Sstevel@tonic-gate 19517c478bd9Sstevel@tonic-gate integrity_fail: 19527c478bd9Sstevel@tonic-gate if (integrity_results != NULL) { 19537c478bd9Sstevel@tonic-gate const char *fname = "/etc/svc/volatile/db_errors"; 19547c478bd9Sstevel@tonic-gate if ((fd = open(fname, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { 19557c478bd9Sstevel@tonic-gate fname = NULL; 19567c478bd9Sstevel@tonic-gate } else { 19577c478bd9Sstevel@tonic-gate if (backend_fd_write(fd, "\n\n") < 0 || 19587c478bd9Sstevel@tonic-gate backend_fd_write(fd, db_file) < 0 || 19597c478bd9Sstevel@tonic-gate backend_fd_write(fd, 19607c478bd9Sstevel@tonic-gate ": PRAGMA integrity_check; failed. Results:\n") < 19617c478bd9Sstevel@tonic-gate 0 || backend_fd_write(fd, integrity_results) < 0 || 19627c478bd9Sstevel@tonic-gate backend_fd_write(fd, "\n\n") < 0) { 19637c478bd9Sstevel@tonic-gate fname = NULL; 19647c478bd9Sstevel@tonic-gate } 19657c478bd9Sstevel@tonic-gate (void) close(fd); 19667c478bd9Sstevel@tonic-gate } 19677c478bd9Sstevel@tonic-gate 19687c478bd9Sstevel@tonic-gate if (!is_main_repository || 19697c478bd9Sstevel@tonic-gate backend_id == BACKEND_TYPE_NONPERSIST) { 19707c478bd9Sstevel@tonic-gate if (fname != NULL) 19717c478bd9Sstevel@tonic-gate configd_critical( 19727c478bd9Sstevel@tonic-gate "%s: integrity check failed. Details in " 19737c478bd9Sstevel@tonic-gate "%s\n", db_file, fname); 19747c478bd9Sstevel@tonic-gate else 19757c478bd9Sstevel@tonic-gate configd_critical( 1976db11989eSpjung "%s: integrity check failed.\n", 19777c478bd9Sstevel@tonic-gate db_file); 19787c478bd9Sstevel@tonic-gate } else { 19797c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 19807c478bd9Sstevel@tonic-gate "\n" 19817c478bd9Sstevel@tonic-gate "svc.configd: smf(5) database integrity check of:\n" 19827c478bd9Sstevel@tonic-gate "\n" 19837c478bd9Sstevel@tonic-gate " %s\n" 19847c478bd9Sstevel@tonic-gate "\n" 19857c478bd9Sstevel@tonic-gate " failed. The database might be damaged or a media error might have\n" 19867c478bd9Sstevel@tonic-gate " prevented it from being verified. Additional information useful to\n" 19877c478bd9Sstevel@tonic-gate " your service provider%s%s\n" 19887c478bd9Sstevel@tonic-gate "\n" 19897c478bd9Sstevel@tonic-gate " The system will not be able to boot until you have restored a working\n" 19907c478bd9Sstevel@tonic-gate " database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n" 19917c478bd9Sstevel@tonic-gate " purposes. The command:\n" 19927c478bd9Sstevel@tonic-gate "\n" 19937c478bd9Sstevel@tonic-gate " /lib/svc/bin/restore_repository\n" 19947c478bd9Sstevel@tonic-gate "\n" 19957c478bd9Sstevel@tonic-gate " can be run to restore a backup version of your repository. See\n" 1996*654b400cSJoshua M. Clulow " http://illumos.org/msg/SMF-8000-MY for more information.\n" 19977c478bd9Sstevel@tonic-gate "\n", 19987c478bd9Sstevel@tonic-gate db_file, 19997c478bd9Sstevel@tonic-gate (fname == NULL)? ":\n\n" : " is in:\n\n ", 20007c478bd9Sstevel@tonic-gate (fname == NULL)? integrity_results : fname); 20017c478bd9Sstevel@tonic-gate } 20027c478bd9Sstevel@tonic-gate free(errp); 20037c478bd9Sstevel@tonic-gate goto fail; 20047c478bd9Sstevel@tonic-gate } 20057c478bd9Sstevel@tonic-gate 20067c478bd9Sstevel@tonic-gate /* 20076e1d2b42Samaguire * Simply do check if backend has been upgraded. We do not wish 20086e1d2b42Samaguire * to actually carry out upgrade here - the main repository may 20096e1d2b42Samaguire * not be writable at this point. Actual upgrade is carried out 20106e1d2b42Samaguire * via backend_check_readonly(). This check is done so that 20116e1d2b42Samaguire * we determine repository state - upgraded or not - and then 20126e1d2b42Samaguire * the appropriate SELECT statement (value-ordered or not) 20136e1d2b42Samaguire * can be used when retrieving property values early in boot. 20146e1d2b42Samaguire */ 20156e1d2b42Samaguire if (backend_id == BACKEND_TYPE_NORMAL) 20166e1d2b42Samaguire backend_check_upgrade(be, B_FALSE); 20176e1d2b42Samaguire /* 20187c478bd9Sstevel@tonic-gate * check if we are writable 20197c478bd9Sstevel@tonic-gate */ 20208918dff3Sjwadams r = backend_is_readonly(be->be_db, be->be_path); 20217c478bd9Sstevel@tonic-gate 20227c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 20237c478bd9Sstevel@tonic-gate free(errp); 20247c478bd9Sstevel@tonic-gate *bep = NULL; 20257c478bd9Sstevel@tonic-gate backend_destroy(be); 20267c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 20277c478bd9Sstevel@tonic-gate } 20287c478bd9Sstevel@tonic-gate if (r != SQLITE_OK && r != SQLITE_FULL) { 20297c478bd9Sstevel@tonic-gate free(errp); 20307c478bd9Sstevel@tonic-gate be->be_readonly = 1; 20317c478bd9Sstevel@tonic-gate *bep = be; 20327c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_READONLY); 20337c478bd9Sstevel@tonic-gate } 20346e1d2b42Samaguire 20357c478bd9Sstevel@tonic-gate *bep = be; 20367c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_SUCCESS); 20377c478bd9Sstevel@tonic-gate 20387c478bd9Sstevel@tonic-gate fail: 20397c478bd9Sstevel@tonic-gate *bep = NULL; 20407c478bd9Sstevel@tonic-gate backend_destroy(be); 20417c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_FAIL); 20427c478bd9Sstevel@tonic-gate } 20437c478bd9Sstevel@tonic-gate 20447c478bd9Sstevel@tonic-gate /* 20457c478bd9Sstevel@tonic-gate * (arg & -arg) is, through the magic of twos-complement arithmetic, the 20467c478bd9Sstevel@tonic-gate * lowest set bit in arg. 20477c478bd9Sstevel@tonic-gate */ 20487c478bd9Sstevel@tonic-gate static size_t 20497c478bd9Sstevel@tonic-gate round_up_to_p2(size_t arg) 20507c478bd9Sstevel@tonic-gate { 20517c478bd9Sstevel@tonic-gate /* 20527c478bd9Sstevel@tonic-gate * Don't allow a zero result. 20537c478bd9Sstevel@tonic-gate */ 20547c478bd9Sstevel@tonic-gate assert(arg > 0 && ((ssize_t)arg > 0)); 20557c478bd9Sstevel@tonic-gate 20567c478bd9Sstevel@tonic-gate while ((arg & (arg - 1)) != 0) 20577c478bd9Sstevel@tonic-gate arg += (arg & -arg); 20587c478bd9Sstevel@tonic-gate 20597c478bd9Sstevel@tonic-gate return (arg); 20607c478bd9Sstevel@tonic-gate } 20617c478bd9Sstevel@tonic-gate 20627c478bd9Sstevel@tonic-gate /* 20637c478bd9Sstevel@tonic-gate * Returns 20647c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 20657c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist 20667c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 20677c478bd9Sstevel@tonic-gate * _SUCCESS 20687c478bd9Sstevel@tonic-gate */ 20697c478bd9Sstevel@tonic-gate int 20707c478bd9Sstevel@tonic-gate backend_run(backend_type_t t, backend_query_t *q, 20717c478bd9Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 20727c478bd9Sstevel@tonic-gate { 20737c478bd9Sstevel@tonic-gate char *errmsg = NULL; 20747c478bd9Sstevel@tonic-gate int ret; 20757c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 20767c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 20777c478bd9Sstevel@tonic-gate 20787c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 20797c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 20807c478bd9Sstevel@tonic-gate 20817c478bd9Sstevel@tonic-gate if ((ret = backend_lock(t, 0, &be)) != REP_PROTOCOL_SUCCESS) 20827c478bd9Sstevel@tonic-gate return (ret); 20837c478bd9Sstevel@tonic-gate 20847c478bd9Sstevel@tonic-gate ts = gethrtime(); 20857c478bd9Sstevel@tonic-gate vts = gethrvtime(); 20867c478bd9Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 20877c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 20887c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 20897c478bd9Sstevel@tonic-gate backend_unlock(be); 20907c478bd9Sstevel@tonic-gate 20917c478bd9Sstevel@tonic-gate return (ret); 20927c478bd9Sstevel@tonic-gate } 20937c478bd9Sstevel@tonic-gate 20947c478bd9Sstevel@tonic-gate /* 20957c478bd9Sstevel@tonic-gate * Starts a "read-only" transaction -- i.e., locks out writers as long 20967c478bd9Sstevel@tonic-gate * as it is active. 20977c478bd9Sstevel@tonic-gate * 20987c478bd9Sstevel@tonic-gate * Fails with 20997c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 21007c478bd9Sstevel@tonic-gate * 21017c478bd9Sstevel@tonic-gate * If t is not _NORMAL, can also fail with 21027c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 21037c478bd9Sstevel@tonic-gate * 21047c478bd9Sstevel@tonic-gate * If writable is true, can also fail with 21057c478bd9Sstevel@tonic-gate * _BACKEND_READONLY 21067c478bd9Sstevel@tonic-gate */ 21077c478bd9Sstevel@tonic-gate static int 21087c478bd9Sstevel@tonic-gate backend_tx_begin_common(backend_type_t t, backend_tx_t **txp, int writable) 21097c478bd9Sstevel@tonic-gate { 21107c478bd9Sstevel@tonic-gate backend_tx_t *ret; 21117c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 21127c478bd9Sstevel@tonic-gate int r; 21137c478bd9Sstevel@tonic-gate 21147c478bd9Sstevel@tonic-gate *txp = NULL; 21157c478bd9Sstevel@tonic-gate 21167c478bd9Sstevel@tonic-gate ret = uu_zalloc(sizeof (*ret)); 21177c478bd9Sstevel@tonic-gate if (ret == NULL) 21187c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 21197c478bd9Sstevel@tonic-gate 21207c478bd9Sstevel@tonic-gate if ((r = backend_lock(t, writable, &be)) != REP_PROTOCOL_SUCCESS) { 21217c478bd9Sstevel@tonic-gate uu_free(ret); 21227c478bd9Sstevel@tonic-gate return (r); 21237c478bd9Sstevel@tonic-gate } 21247c478bd9Sstevel@tonic-gate 21257c478bd9Sstevel@tonic-gate ret->bt_be = be; 21267c478bd9Sstevel@tonic-gate ret->bt_readonly = !writable; 21277c478bd9Sstevel@tonic-gate ret->bt_type = t; 21287c478bd9Sstevel@tonic-gate ret->bt_full = 0; 21297c478bd9Sstevel@tonic-gate 21307c478bd9Sstevel@tonic-gate *txp = ret; 21317c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 21327c478bd9Sstevel@tonic-gate } 21337c478bd9Sstevel@tonic-gate 21347c478bd9Sstevel@tonic-gate int 21357c478bd9Sstevel@tonic-gate backend_tx_begin_ro(backend_type_t t, backend_tx_t **txp) 21367c478bd9Sstevel@tonic-gate { 21377c478bd9Sstevel@tonic-gate return (backend_tx_begin_common(t, txp, 0)); 21387c478bd9Sstevel@tonic-gate } 21397c478bd9Sstevel@tonic-gate 21407c478bd9Sstevel@tonic-gate static void 21417c478bd9Sstevel@tonic-gate backend_tx_end(backend_tx_t *tx) 21427c478bd9Sstevel@tonic-gate { 21437c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 21447c478bd9Sstevel@tonic-gate 21457c478bd9Sstevel@tonic-gate be = tx->bt_be; 21467c478bd9Sstevel@tonic-gate 21477c478bd9Sstevel@tonic-gate if (tx->bt_full) { 21487c478bd9Sstevel@tonic-gate struct sqlite *new; 21497c478bd9Sstevel@tonic-gate 21507c478bd9Sstevel@tonic-gate /* 21517c478bd9Sstevel@tonic-gate * sqlite tends to be sticky with SQLITE_FULL, so we try 21527c478bd9Sstevel@tonic-gate * to get a fresh database handle if we got a FULL warning 21537c478bd9Sstevel@tonic-gate * along the way. If that fails, no harm done. 21547c478bd9Sstevel@tonic-gate */ 21557c478bd9Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, NULL); 21567c478bd9Sstevel@tonic-gate if (new != NULL) { 21577c478bd9Sstevel@tonic-gate sqlite_close(be->be_db); 21587c478bd9Sstevel@tonic-gate be->be_db = new; 21597c478bd9Sstevel@tonic-gate } 21607c478bd9Sstevel@tonic-gate } 21617c478bd9Sstevel@tonic-gate backend_unlock(be); 21627c478bd9Sstevel@tonic-gate tx->bt_be = NULL; 21637c478bd9Sstevel@tonic-gate uu_free(tx); 21647c478bd9Sstevel@tonic-gate } 21657c478bd9Sstevel@tonic-gate 21667c478bd9Sstevel@tonic-gate void 21677c478bd9Sstevel@tonic-gate backend_tx_end_ro(backend_tx_t *tx) 21687c478bd9Sstevel@tonic-gate { 21697c478bd9Sstevel@tonic-gate assert(tx->bt_readonly); 21707c478bd9Sstevel@tonic-gate backend_tx_end(tx); 21717c478bd9Sstevel@tonic-gate } 21727c478bd9Sstevel@tonic-gate 21737c478bd9Sstevel@tonic-gate /* 21747c478bd9Sstevel@tonic-gate * Fails with 21757c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 21767c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS 21777c478bd9Sstevel@tonic-gate * _BACKEND_READONLY 21787c478bd9Sstevel@tonic-gate */ 21797c478bd9Sstevel@tonic-gate int 21807c478bd9Sstevel@tonic-gate backend_tx_begin(backend_type_t t, backend_tx_t **txp) 21817c478bd9Sstevel@tonic-gate { 21827c478bd9Sstevel@tonic-gate int r; 21837c478bd9Sstevel@tonic-gate char *errmsg; 21847c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 21857c478bd9Sstevel@tonic-gate 21867c478bd9Sstevel@tonic-gate r = backend_tx_begin_common(t, txp, 1); 21877c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 21887c478bd9Sstevel@tonic-gate return (r); 21897c478bd9Sstevel@tonic-gate 21907c478bd9Sstevel@tonic-gate ts = gethrtime(); 21917c478bd9Sstevel@tonic-gate vts = gethrvtime(); 21927c478bd9Sstevel@tonic-gate r = sqlite_exec((*txp)->bt_be->be_db, "BEGIN TRANSACTION", NULL, NULL, 21937c478bd9Sstevel@tonic-gate &errmsg); 21947c478bd9Sstevel@tonic-gate UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); 21957c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 21967c478bd9Sstevel@tonic-gate (*txp)->bt_full = 1; 21977c478bd9Sstevel@tonic-gate r = backend_error((*txp)->bt_be, r, errmsg); 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 22007c478bd9Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 22017c478bd9Sstevel@tonic-gate (void) sqlite_exec((*txp)->bt_be->be_db, 22027c478bd9Sstevel@tonic-gate "ROLLBACK TRANSACTION", NULL, NULL, NULL); 22037c478bd9Sstevel@tonic-gate backend_tx_end(*txp); 22047c478bd9Sstevel@tonic-gate *txp = NULL; 22057c478bd9Sstevel@tonic-gate return (r); 22067c478bd9Sstevel@tonic-gate } 22077c478bd9Sstevel@tonic-gate 22087c478bd9Sstevel@tonic-gate (*txp)->bt_readonly = 0; 22097c478bd9Sstevel@tonic-gate 22107c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 22117c478bd9Sstevel@tonic-gate } 22127c478bd9Sstevel@tonic-gate 22137c478bd9Sstevel@tonic-gate void 22147c478bd9Sstevel@tonic-gate backend_tx_rollback(backend_tx_t *tx) 22157c478bd9Sstevel@tonic-gate { 22167c478bd9Sstevel@tonic-gate int r; 22177c478bd9Sstevel@tonic-gate char *errmsg; 22187c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 22197c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 22207c478bd9Sstevel@tonic-gate 22217c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 22227c478bd9Sstevel@tonic-gate be = tx->bt_be; 22237c478bd9Sstevel@tonic-gate 22247c478bd9Sstevel@tonic-gate ts = gethrtime(); 22257c478bd9Sstevel@tonic-gate vts = gethrvtime(); 22267c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 22277c478bd9Sstevel@tonic-gate &errmsg); 22287c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 22297c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 22307c478bd9Sstevel@tonic-gate tx->bt_full = 1; 22317c478bd9Sstevel@tonic-gate (void) backend_error(be, r, errmsg); 22327c478bd9Sstevel@tonic-gate 22337c478bd9Sstevel@tonic-gate backend_tx_end(tx); 22347c478bd9Sstevel@tonic-gate } 22357c478bd9Sstevel@tonic-gate 22367c478bd9Sstevel@tonic-gate /* 22377c478bd9Sstevel@tonic-gate * Fails with 22387c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 22397c478bd9Sstevel@tonic-gate */ 22407c478bd9Sstevel@tonic-gate int 22417c478bd9Sstevel@tonic-gate backend_tx_commit(backend_tx_t *tx) 22427c478bd9Sstevel@tonic-gate { 22437c478bd9Sstevel@tonic-gate int r, r2; 22447c478bd9Sstevel@tonic-gate char *errmsg; 22457c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 22467c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 22477c478bd9Sstevel@tonic-gate 22487c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 22497c478bd9Sstevel@tonic-gate be = tx->bt_be; 22507c478bd9Sstevel@tonic-gate ts = gethrtime(); 22517c478bd9Sstevel@tonic-gate vts = gethrvtime(); 22527c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "COMMIT TRANSACTION", NULL, NULL, 22537c478bd9Sstevel@tonic-gate &errmsg); 22547c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 22557c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 22567c478bd9Sstevel@tonic-gate tx->bt_full = 1; 22577c478bd9Sstevel@tonic-gate 22587c478bd9Sstevel@tonic-gate r = backend_error(be, r, errmsg); 22597c478bd9Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 22607c478bd9Sstevel@tonic-gate 22617c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 22627c478bd9Sstevel@tonic-gate r2 = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 22637c478bd9Sstevel@tonic-gate &errmsg); 22647c478bd9Sstevel@tonic-gate r2 = backend_error(be, r2, errmsg); 22657c478bd9Sstevel@tonic-gate if (r2 != REP_PROTOCOL_SUCCESS) 22667c478bd9Sstevel@tonic-gate backend_panic("cannot rollback failed commit"); 22677c478bd9Sstevel@tonic-gate 22687c478bd9Sstevel@tonic-gate backend_tx_end(tx); 22697c478bd9Sstevel@tonic-gate return (r); 22707c478bd9Sstevel@tonic-gate } 22717c478bd9Sstevel@tonic-gate backend_tx_end(tx); 22727c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 22737c478bd9Sstevel@tonic-gate } 22747c478bd9Sstevel@tonic-gate 22757c478bd9Sstevel@tonic-gate static const char * 22767c478bd9Sstevel@tonic-gate id_space_to_name(enum id_space id) 22777c478bd9Sstevel@tonic-gate { 22787c478bd9Sstevel@tonic-gate switch (id) { 22797c478bd9Sstevel@tonic-gate case BACKEND_ID_SERVICE_INSTANCE: 22807c478bd9Sstevel@tonic-gate return ("SI"); 22817c478bd9Sstevel@tonic-gate case BACKEND_ID_PROPERTYGRP: 22827c478bd9Sstevel@tonic-gate return ("PG"); 22837c478bd9Sstevel@tonic-gate case BACKEND_ID_GENERATION: 22847c478bd9Sstevel@tonic-gate return ("GEN"); 22857c478bd9Sstevel@tonic-gate case BACKEND_ID_PROPERTY: 22867c478bd9Sstevel@tonic-gate return ("PROP"); 22877c478bd9Sstevel@tonic-gate case BACKEND_ID_VALUE: 22887c478bd9Sstevel@tonic-gate return ("VAL"); 22897c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPNAME: 22907c478bd9Sstevel@tonic-gate return ("SNAME"); 22917c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPSHOT: 22927c478bd9Sstevel@tonic-gate return ("SHOT"); 22937c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPLEVEL: 22947c478bd9Sstevel@tonic-gate return ("SLVL"); 22957c478bd9Sstevel@tonic-gate default: 22967c478bd9Sstevel@tonic-gate abort(); 22977c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 22987c478bd9Sstevel@tonic-gate } 22997c478bd9Sstevel@tonic-gate } 23007c478bd9Sstevel@tonic-gate 23017c478bd9Sstevel@tonic-gate /* 23027c478bd9Sstevel@tonic-gate * Returns a new id or 0 if the id argument is invalid or the query fails. 23037c478bd9Sstevel@tonic-gate */ 23047c478bd9Sstevel@tonic-gate uint32_t 23057c478bd9Sstevel@tonic-gate backend_new_id(backend_tx_t *tx, enum id_space id) 23067c478bd9Sstevel@tonic-gate { 23077c478bd9Sstevel@tonic-gate struct run_single_int_info info; 23087c478bd9Sstevel@tonic-gate uint32_t new_id = 0; 23097c478bd9Sstevel@tonic-gate const char *name = id_space_to_name(id); 23107c478bd9Sstevel@tonic-gate char *errmsg; 23117c478bd9Sstevel@tonic-gate int ret; 23127c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 23137c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 23147c478bd9Sstevel@tonic-gate 23157c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 23167c478bd9Sstevel@tonic-gate be = tx->bt_be; 23177c478bd9Sstevel@tonic-gate 23187c478bd9Sstevel@tonic-gate info.rs_out = &new_id; 23197c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 23207c478bd9Sstevel@tonic-gate 23217c478bd9Sstevel@tonic-gate ts = gethrtime(); 23227c478bd9Sstevel@tonic-gate vts = gethrvtime(); 23237c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 23247c478bd9Sstevel@tonic-gate "SELECT id_next FROM id_tbl WHERE (id_name = '%q');" 23257c478bd9Sstevel@tonic-gate "UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');", 23267c478bd9Sstevel@tonic-gate run_single_int_callback, &info, &errmsg, name, name); 23277c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 23287c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 23297c478bd9Sstevel@tonic-gate tx->bt_full = 1; 23307c478bd9Sstevel@tonic-gate 23317c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 23327c478bd9Sstevel@tonic-gate 23337c478bd9Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) { 23347c478bd9Sstevel@tonic-gate return (0); 23357c478bd9Sstevel@tonic-gate } 23367c478bd9Sstevel@tonic-gate 23377c478bd9Sstevel@tonic-gate return (new_id); 23387c478bd9Sstevel@tonic-gate } 23397c478bd9Sstevel@tonic-gate 23407c478bd9Sstevel@tonic-gate /* 23417c478bd9Sstevel@tonic-gate * Returns 23427c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 23437c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 23447c478bd9Sstevel@tonic-gate * _SUCCESS 23457c478bd9Sstevel@tonic-gate */ 23467c478bd9Sstevel@tonic-gate int 23477c478bd9Sstevel@tonic-gate backend_tx_run(backend_tx_t *tx, backend_query_t *q, 23487c478bd9Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 23497c478bd9Sstevel@tonic-gate { 23507c478bd9Sstevel@tonic-gate char *errmsg = NULL; 23517c478bd9Sstevel@tonic-gate int ret; 23527c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 23537c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 23547c478bd9Sstevel@tonic-gate 23557c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL); 23567c478bd9Sstevel@tonic-gate be = tx->bt_be; 23577c478bd9Sstevel@tonic-gate 23587c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 23597c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 23607c478bd9Sstevel@tonic-gate 23617c478bd9Sstevel@tonic-gate ts = gethrtime(); 23627c478bd9Sstevel@tonic-gate vts = gethrvtime(); 23637c478bd9Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 23647c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 23657c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 23667c478bd9Sstevel@tonic-gate tx->bt_full = 1; 23677c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 23687c478bd9Sstevel@tonic-gate 23697c478bd9Sstevel@tonic-gate return (ret); 23707c478bd9Sstevel@tonic-gate } 23717c478bd9Sstevel@tonic-gate 23727c478bd9Sstevel@tonic-gate /* 23737c478bd9Sstevel@tonic-gate * Returns 23747c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 23757c478bd9Sstevel@tonic-gate * _NOT_FOUND - the query returned no results 23767c478bd9Sstevel@tonic-gate * _SUCCESS - the query returned a single integer 23777c478bd9Sstevel@tonic-gate */ 23787c478bd9Sstevel@tonic-gate int 23797c478bd9Sstevel@tonic-gate backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q, uint32_t *buf) 23807c478bd9Sstevel@tonic-gate { 23817c478bd9Sstevel@tonic-gate struct run_single_int_info info; 23827c478bd9Sstevel@tonic-gate int ret; 23837c478bd9Sstevel@tonic-gate 23847c478bd9Sstevel@tonic-gate info.rs_out = buf; 23857c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 23867c478bd9Sstevel@tonic-gate 23877c478bd9Sstevel@tonic-gate ret = backend_tx_run(tx, q, run_single_int_callback, &info); 23887c478bd9Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 23897c478bd9Sstevel@tonic-gate 23907c478bd9Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 23917c478bd9Sstevel@tonic-gate return (ret); 23927c478bd9Sstevel@tonic-gate 23937c478bd9Sstevel@tonic-gate return (info.rs_result); 23947c478bd9Sstevel@tonic-gate } 23957c478bd9Sstevel@tonic-gate 23967c478bd9Sstevel@tonic-gate /* 23977c478bd9Sstevel@tonic-gate * Fails with 23987c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 23997c478bd9Sstevel@tonic-gate */ 24007c478bd9Sstevel@tonic-gate int 24017c478bd9Sstevel@tonic-gate backend_tx_run_update(backend_tx_t *tx, const char *format, ...) 24027c478bd9Sstevel@tonic-gate { 24037c478bd9Sstevel@tonic-gate va_list a; 24047c478bd9Sstevel@tonic-gate char *errmsg; 24057c478bd9Sstevel@tonic-gate int ret; 24067c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 24077c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 24087c478bd9Sstevel@tonic-gate 24097c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 24107c478bd9Sstevel@tonic-gate be = tx->bt_be; 24117c478bd9Sstevel@tonic-gate 24127c478bd9Sstevel@tonic-gate va_start(a, format); 24137c478bd9Sstevel@tonic-gate ts = gethrtime(); 24147c478bd9Sstevel@tonic-gate vts = gethrvtime(); 24157c478bd9Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 24167c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 24177c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 24187c478bd9Sstevel@tonic-gate tx->bt_full = 1; 24197c478bd9Sstevel@tonic-gate va_end(a); 24207c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 24217c478bd9Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 24227c478bd9Sstevel@tonic-gate 24237c478bd9Sstevel@tonic-gate return (ret); 24247c478bd9Sstevel@tonic-gate } 24257c478bd9Sstevel@tonic-gate 24267c478bd9Sstevel@tonic-gate /* 24277c478bd9Sstevel@tonic-gate * returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured 24287c478bd9Sstevel@tonic-gate */ 24297c478bd9Sstevel@tonic-gate int 24307c478bd9Sstevel@tonic-gate backend_tx_run_update_changed(backend_tx_t *tx, const char *format, ...) 24317c478bd9Sstevel@tonic-gate { 24327c478bd9Sstevel@tonic-gate va_list a; 24337c478bd9Sstevel@tonic-gate char *errmsg; 24347c478bd9Sstevel@tonic-gate int ret; 24357c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 24367c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 24377c478bd9Sstevel@tonic-gate 24387c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 24397c478bd9Sstevel@tonic-gate be = tx->bt_be; 24407c478bd9Sstevel@tonic-gate 24417c478bd9Sstevel@tonic-gate va_start(a, format); 24427c478bd9Sstevel@tonic-gate ts = gethrtime(); 24437c478bd9Sstevel@tonic-gate vts = gethrvtime(); 24447c478bd9Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 24457c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 24467c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 24477c478bd9Sstevel@tonic-gate tx->bt_full = 1; 24487c478bd9Sstevel@tonic-gate va_end(a); 24497c478bd9Sstevel@tonic-gate 24507c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 24517c478bd9Sstevel@tonic-gate 24527c478bd9Sstevel@tonic-gate return (ret); 24537c478bd9Sstevel@tonic-gate } 24547c478bd9Sstevel@tonic-gate 24557c478bd9Sstevel@tonic-gate #define BACKEND_ADD_SCHEMA(be, file, tbls, idxs) \ 24567c478bd9Sstevel@tonic-gate (backend_add_schema((be), (file), \ 24577c478bd9Sstevel@tonic-gate (tbls), sizeof (tbls) / sizeof (*(tbls)), \ 24587c478bd9Sstevel@tonic-gate (idxs), sizeof (idxs) / sizeof (*(idxs)))) 24597c478bd9Sstevel@tonic-gate 24607c478bd9Sstevel@tonic-gate static int 24617c478bd9Sstevel@tonic-gate backend_add_schema(sqlite_backend_t *be, const char *file, 24627c478bd9Sstevel@tonic-gate struct backend_tbl_info *tbls, int tbl_count, 24637c478bd9Sstevel@tonic-gate struct backend_idx_info *idxs, int idx_count) 24647c478bd9Sstevel@tonic-gate { 24657c478bd9Sstevel@tonic-gate int i; 24667c478bd9Sstevel@tonic-gate char *errmsg; 24677c478bd9Sstevel@tonic-gate int ret; 24687c478bd9Sstevel@tonic-gate 24697c478bd9Sstevel@tonic-gate /* 24707c478bd9Sstevel@tonic-gate * Create the tables. 24717c478bd9Sstevel@tonic-gate */ 24727c478bd9Sstevel@tonic-gate for (i = 0; i < tbl_count; i++) { 24737c478bd9Sstevel@tonic-gate if (tbls[i].bti_name == NULL) { 24747c478bd9Sstevel@tonic-gate assert(i + 1 == tbl_count); 24757c478bd9Sstevel@tonic-gate break; 24767c478bd9Sstevel@tonic-gate } 24777c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 24787c478bd9Sstevel@tonic-gate "CREATE TABLE %s (%s);\n", 24797c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, tbls[i].bti_name, tbls[i].bti_cols); 24807c478bd9Sstevel@tonic-gate 24817c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 24827c478bd9Sstevel@tonic-gate configd_critical( 24837c478bd9Sstevel@tonic-gate "%s: %s table creation fails: %s\n", file, 24847c478bd9Sstevel@tonic-gate tbls[i].bti_name, errmsg); 24857c478bd9Sstevel@tonic-gate free(errmsg); 24867c478bd9Sstevel@tonic-gate return (-1); 24877c478bd9Sstevel@tonic-gate } 24887c478bd9Sstevel@tonic-gate } 24897c478bd9Sstevel@tonic-gate 24907c478bd9Sstevel@tonic-gate /* 24917c478bd9Sstevel@tonic-gate * Make indices on key tables and columns. 24927c478bd9Sstevel@tonic-gate */ 24937c478bd9Sstevel@tonic-gate for (i = 0; i < idx_count; i++) { 24947c478bd9Sstevel@tonic-gate if (idxs[i].bxi_tbl == NULL) { 24957c478bd9Sstevel@tonic-gate assert(i + 1 == idx_count); 24967c478bd9Sstevel@tonic-gate break; 24977c478bd9Sstevel@tonic-gate } 24987c478bd9Sstevel@tonic-gate 24997c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 25007c478bd9Sstevel@tonic-gate "CREATE INDEX %s_%s ON %s (%s);\n", 25017c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, idxs[i].bxi_tbl, idxs[i].bxi_idx, 25027c478bd9Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_cols); 25037c478bd9Sstevel@tonic-gate 25047c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 25057c478bd9Sstevel@tonic-gate configd_critical( 25067c478bd9Sstevel@tonic-gate "%s: %s_%s index creation fails: %s\n", file, 25077c478bd9Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_idx, errmsg); 25087c478bd9Sstevel@tonic-gate free(errmsg); 25097c478bd9Sstevel@tonic-gate return (-1); 25107c478bd9Sstevel@tonic-gate } 25117c478bd9Sstevel@tonic-gate } 25127c478bd9Sstevel@tonic-gate return (0); 25137c478bd9Sstevel@tonic-gate } 25147c478bd9Sstevel@tonic-gate 25157c478bd9Sstevel@tonic-gate static int 25167c478bd9Sstevel@tonic-gate backend_init_schema(sqlite_backend_t *be, const char *db_file, backend_type_t t) 25177c478bd9Sstevel@tonic-gate { 25187c478bd9Sstevel@tonic-gate int i; 25197c478bd9Sstevel@tonic-gate char *errmsg; 25207c478bd9Sstevel@tonic-gate int ret; 25217c478bd9Sstevel@tonic-gate 25227c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || t == BACKEND_TYPE_NONPERSIST); 25237c478bd9Sstevel@tonic-gate 25247c478bd9Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) { 25257c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_normal, idxs_normal); 25267c478bd9Sstevel@tonic-gate } else if (t == BACKEND_TYPE_NONPERSIST) { 25277c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_np, idxs_np); 25287c478bd9Sstevel@tonic-gate } else { 25297c478bd9Sstevel@tonic-gate abort(); /* can't happen */ 25307c478bd9Sstevel@tonic-gate } 25317c478bd9Sstevel@tonic-gate 25327c478bd9Sstevel@tonic-gate if (ret < 0) { 25337c478bd9Sstevel@tonic-gate return (ret); 25347c478bd9Sstevel@tonic-gate } 25357c478bd9Sstevel@tonic-gate 25367c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_common, idxs_common); 25377c478bd9Sstevel@tonic-gate if (ret < 0) { 25387c478bd9Sstevel@tonic-gate return (ret); 25397c478bd9Sstevel@tonic-gate } 25407c478bd9Sstevel@tonic-gate 25417c478bd9Sstevel@tonic-gate /* 25427c478bd9Sstevel@tonic-gate * Add the schema version to the table 25437c478bd9Sstevel@tonic-gate */ 25447c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 25457c478bd9Sstevel@tonic-gate "INSERT INTO schema_version (schema_version) VALUES (%d)", 25467c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, BACKEND_SCHEMA_VERSION); 25477c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 25487c478bd9Sstevel@tonic-gate configd_critical( 25497c478bd9Sstevel@tonic-gate "setting schema version fails: %s\n", errmsg); 25507c478bd9Sstevel@tonic-gate free(errmsg); 25517c478bd9Sstevel@tonic-gate } 25527c478bd9Sstevel@tonic-gate 25537c478bd9Sstevel@tonic-gate /* 25547c478bd9Sstevel@tonic-gate * Populate id_tbl with initial IDs. 25557c478bd9Sstevel@tonic-gate */ 25567c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_ID_INVALID; i++) { 25577c478bd9Sstevel@tonic-gate const char *name = id_space_to_name(i); 25587c478bd9Sstevel@tonic-gate 25597c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 25607c478bd9Sstevel@tonic-gate "INSERT INTO id_tbl (id_name, id_next) " 25617c478bd9Sstevel@tonic-gate "VALUES ('%q', %d);", NULL, NULL, &errmsg, name, 1); 25627c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 25637c478bd9Sstevel@tonic-gate configd_critical( 25647c478bd9Sstevel@tonic-gate "id insertion for %s fails: %s\n", name, errmsg); 25657c478bd9Sstevel@tonic-gate free(errmsg); 25667c478bd9Sstevel@tonic-gate return (-1); 25677c478bd9Sstevel@tonic-gate } 25687c478bd9Sstevel@tonic-gate } 25697c478bd9Sstevel@tonic-gate /* 25707c478bd9Sstevel@tonic-gate * Set the persistance of the database. The normal database is marked 25717c478bd9Sstevel@tonic-gate * "synchronous", so that all writes are synchronized to stable storage 25727c478bd9Sstevel@tonic-gate * before proceeding. 25737c478bd9Sstevel@tonic-gate */ 25747c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 25757c478bd9Sstevel@tonic-gate "PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;", 25767c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, 25777c478bd9Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF", 25787c478bd9Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF"); 25797c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 25807c478bd9Sstevel@tonic-gate configd_critical("pragma setting fails: %s\n", errmsg); 25817c478bd9Sstevel@tonic-gate free(errmsg); 25827c478bd9Sstevel@tonic-gate return (-1); 25837c478bd9Sstevel@tonic-gate } 25847c478bd9Sstevel@tonic-gate 25857c478bd9Sstevel@tonic-gate return (0); 25867c478bd9Sstevel@tonic-gate } 25877c478bd9Sstevel@tonic-gate 25887c478bd9Sstevel@tonic-gate int 25897c478bd9Sstevel@tonic-gate backend_init(const char *db_file, const char *npdb_file, int have_np) 25907c478bd9Sstevel@tonic-gate { 25917c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 25929444c26fSTom Whitten char *errp; 25939444c26fSTom Whitten struct sqlite *fast_db; 25947c478bd9Sstevel@tonic-gate int r; 25959444c26fSTom Whitten backend_switch_results_t switch_result = BACKEND_SWITCH_OK; 25967c478bd9Sstevel@tonic-gate int writable_persist = 1; 25977c478bd9Sstevel@tonic-gate 25987c478bd9Sstevel@tonic-gate /* set up our temporary directory */ 25997c478bd9Sstevel@tonic-gate sqlite_temp_directory = "/etc/svc/volatile"; 26007c478bd9Sstevel@tonic-gate 26017c478bd9Sstevel@tonic-gate if (strcmp(SQLITE_VERSION, sqlite_version) != 0) { 26027c478bd9Sstevel@tonic-gate configd_critical("Mismatched link! (%s should be %s)\n", 26037c478bd9Sstevel@tonic-gate sqlite_version, SQLITE_VERSION); 26047c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 26057c478bd9Sstevel@tonic-gate } 2606c0889d7aSstevep 26077c478bd9Sstevel@tonic-gate if (db_file == NULL) 26087c478bd9Sstevel@tonic-gate db_file = REPOSITORY_DB; 26095b7f77adStw21770 if (strcmp(db_file, REPOSITORY_DB) != 0) { 26105b7f77adStw21770 is_main_repository = 0; 26115b7f77adStw21770 } 26127c478bd9Sstevel@tonic-gate 26139444c26fSTom Whitten /* 26149444c26fSTom Whitten * If the svc.configd crashed, there might be a leftover transient 26159444c26fSTom Whitten * database at FAST_REPOSITORY_DB,which contains useful 26169444c26fSTom Whitten * information. Both early manifest import and late manifest 26179444c26fSTom Whitten * import use svcadm to copy the repository to FAST_REPOSITORY_DB. 26189444c26fSTom Whitten * One reason for doing this is that it improves the performance of 26199444c26fSTom Whitten * manifest import. The other reason is that the repository may be 26209444c26fSTom Whitten * on read-only root in the case of early manifest import. 26219444c26fSTom Whitten * 26229444c26fSTom Whitten * If FAST_REPOSITORY_DB exists, it is an indication that 26239444c26fSTom Whitten * svc.configd has been restarted for some reason. Since we have 26249444c26fSTom Whitten * no way of knowing where we are in the boot process, the safe 26259444c26fSTom Whitten * thing to do is to move the repository back to it's non-transient 26269444c26fSTom Whitten * location, REPOSITORY_DB. This may slow manifest import 26279444c26fSTom Whitten * performance, but it avoids the problem of missing the command to 26289444c26fSTom Whitten * move the repository to permanent storage. 26299444c26fSTom Whitten * 26309444c26fSTom Whitten * There is a caveat, though. If root is read-only, we'll need to 26319444c26fSTom Whitten * leave the repository at FAST_REPOSITORY_DB. If root is 26329444c26fSTom Whitten * read-only, late manifest import has not yet run, so it will move 26339444c26fSTom Whitten * the repository back to permanent storage when it runs. 26349444c26fSTom Whitten */ 26359444c26fSTom Whitten if (is_main_repository) 26369444c26fSTom Whitten switch_result = backend_switch_recovery(); 26379444c26fSTom Whitten 26387c478bd9Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be); 26397c478bd9Sstevel@tonic-gate switch (r) { 26407c478bd9Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 26417c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 26427c478bd9Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 26437c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 26447c478bd9Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 26457c478bd9Sstevel@tonic-gate break; /* success */ 26467c478bd9Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 26477c478bd9Sstevel@tonic-gate writable_persist = 0; 26487c478bd9Sstevel@tonic-gate break; 26497c478bd9Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 26507c478bd9Sstevel@tonic-gate if (backend_init_schema(be, db_file, BACKEND_TYPE_NORMAL)) { 26517c478bd9Sstevel@tonic-gate backend_destroy(be); 26527c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 26537c478bd9Sstevel@tonic-gate } 26547c478bd9Sstevel@tonic-gate break; 26557c478bd9Sstevel@tonic-gate default: 26567c478bd9Sstevel@tonic-gate abort(); 26577c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 26587c478bd9Sstevel@tonic-gate } 26597c478bd9Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NORMAL, be); 26609444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_REPO_CREATE, 26619444c26fSTom Whitten writable_persist == 1 ? BE_FLIGHT_ST_RW : BE_FLIGHT_ST_RO); 26629444c26fSTom Whitten /* 26639444c26fSTom Whitten * If there was a transient repository that could not be copied 26649444c26fSTom Whitten * back because the root file system was read-only, switch over to 26659444c26fSTom Whitten * using the transient repository. 26669444c26fSTom Whitten */ 26679444c26fSTom Whitten if (switch_result == BACKEND_SWITCH_RO) { 26689444c26fSTom Whitten char *db_name_copy = NULL; 26699444c26fSTom Whitten 26709444c26fSTom Whitten fast_db = sqlite_open(FAST_REPOSITORY_DB, 0600, &errp); 26719444c26fSTom Whitten if (fast_db == NULL) { 26729444c26fSTom Whitten /* Can't open fast repository. Stick with permanent. */ 26739444c26fSTom Whitten configd_critical("Cannot open \"%s\". %s\n", 26749444c26fSTom Whitten FAST_REPOSITORY_DB, errp == NULL ? "" : errp); 26759444c26fSTom Whitten free(errp); 26769444c26fSTom Whitten } else { 26779444c26fSTom Whitten db_name_copy = strdup(FAST_REPOSITORY_DB); 26789444c26fSTom Whitten if (db_name_copy == NULL) { 26799444c26fSTom Whitten configd_critical("backend_init: out of " 26809444c26fSTom Whitten "memory.\n"); 26819444c26fSTom Whitten sqlite_close(fast_db); 26829444c26fSTom Whitten return (CONFIGD_EXIT_INIT_FAILED); 26839444c26fSTom Whitten } else { 26849444c26fSTom Whitten flight_recorder_event( 26859444c26fSTom Whitten BE_FLIGHT_EV_LINGERING_FAST, 26869444c26fSTom Whitten BE_FLIGHT_ST_RO); 26879444c26fSTom Whitten sqlite_close(be->be_db); 26889444c26fSTom Whitten be->be_db = fast_db; 26899444c26fSTom Whitten be->be_ppath = be->be_path; 26909444c26fSTom Whitten be->be_path = db_name_copy; 26919444c26fSTom Whitten } 26929444c26fSTom Whitten } 26939444c26fSTom Whitten } 26947c478bd9Sstevel@tonic-gate 26957c478bd9Sstevel@tonic-gate if (have_np) { 26967c478bd9Sstevel@tonic-gate if (npdb_file == NULL) 26977c478bd9Sstevel@tonic-gate npdb_file = NONPERSIST_DB; 26987c478bd9Sstevel@tonic-gate 26997c478bd9Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NONPERSIST, npdb_file, &be); 27007c478bd9Sstevel@tonic-gate switch (r) { 27017c478bd9Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 27027c478bd9Sstevel@tonic-gate break; /* success */ 27037c478bd9Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 27047c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 27057c478bd9Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 27067c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 27077c478bd9Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 27087c478bd9Sstevel@tonic-gate configd_critical("%s: unable to write\n", npdb_file); 27097c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 27107c478bd9Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 27117c478bd9Sstevel@tonic-gate if (backend_init_schema(be, db_file, 27127c478bd9Sstevel@tonic-gate BACKEND_TYPE_NONPERSIST)) { 27137c478bd9Sstevel@tonic-gate backend_destroy(be); 27147c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 27157c478bd9Sstevel@tonic-gate } 27167c478bd9Sstevel@tonic-gate break; 27177c478bd9Sstevel@tonic-gate default: 27187c478bd9Sstevel@tonic-gate abort(); 27197c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 27207c478bd9Sstevel@tonic-gate } 27217c478bd9Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NONPERSIST, be); 27227c478bd9Sstevel@tonic-gate 27239444c26fSTom Whitten if (r != BACKEND_CREATE_NEED_INIT) { 27249444c26fSTom Whitten flight_recorder_event(BE_FLIGHT_EV_RESTART, 27259444c26fSTom Whitten BE_FLIGHT_ST_INFO); 27269444c26fSTom Whitten } 27279444c26fSTom Whitten 27287c478bd9Sstevel@tonic-gate /* 27297c478bd9Sstevel@tonic-gate * If we started up with a writable filesystem, but the 27309444c26fSTom Whitten * non-persistent database needed initialization, we are 27319444c26fSTom Whitten * booting a non-global zone or a system with a writable 27329444c26fSTom Whitten * root (ZFS), so do a backup. Checking to see if the 27339444c26fSTom Whitten * non-persistent database needed initialization also keeps 27349444c26fSTom Whitten * us from making additional backups if configd gets 27359444c26fSTom Whitten * restarted. 27367c478bd9Sstevel@tonic-gate */ 27377c478bd9Sstevel@tonic-gate if (r == BACKEND_CREATE_NEED_INIT && writable_persist && 27387c478bd9Sstevel@tonic-gate backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 27397c478bd9Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 27407c478bd9Sstevel@tonic-gate if (backend_create_backup_locked(be, 27417c478bd9Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { 27427c478bd9Sstevel@tonic-gate configd_critical( 27437c478bd9Sstevel@tonic-gate "unable to create \"%s\" backup of " 27447c478bd9Sstevel@tonic-gate "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 27457c478bd9Sstevel@tonic-gate be->be_path); 27467c478bd9Sstevel@tonic-gate } 27477c478bd9Sstevel@tonic-gate backend_unlock(be); 27487c478bd9Sstevel@tonic-gate } 27499444c26fSTom Whitten 27509444c26fSTom Whitten /* 27519444c26fSTom Whitten * On the other hand if we started with a read-only file 27529444c26fSTom Whitten * system and the non-persistent database needed 27539444c26fSTom Whitten * initialization, then we need to take a checkpoint of the 27549444c26fSTom Whitten * repository. We grab the checkpoint now before Early 27559444c26fSTom Whitten * Manifest Import starts modifying the repository. Then 27569444c26fSTom Whitten * when the file system becomes writable, the checkpoint 27579444c26fSTom Whitten * can be used to create the boot time backup of the 27589444c26fSTom Whitten * repository. Checking that the non-persistent database 27599444c26fSTom Whitten * needed initialization, keeps us from making additional 27609444c26fSTom Whitten * checkpoints if configd gets restarted. 27619444c26fSTom Whitten */ 27629444c26fSTom Whitten if (r == BACKEND_CREATE_NEED_INIT && writable_persist == 0 && 27639444c26fSTom Whitten backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 27649444c26fSTom Whitten REP_PROTOCOL_SUCCESS) { 27659444c26fSTom Whitten r = backend_checkpoint_repository(be); 27669444c26fSTom Whitten if (r != REP_PROTOCOL_SUCCESS) { 27679444c26fSTom Whitten configd_critical("unable to create checkpoint " 27689444c26fSTom Whitten "of \"%s\"\n", be->be_path); 27699444c26fSTom Whitten } 27709444c26fSTom Whitten backend_unlock(be); 27719444c26fSTom Whitten } 27729444c26fSTom Whitten 27739444c26fSTom Whitten /* 27749444c26fSTom Whitten * If the non-persistent database did not need 27759444c26fSTom Whitten * initialization, svc.configd has been restarted. See if 27769444c26fSTom Whitten * the boot time checkpoint exists. If it does, use it to 27779444c26fSTom Whitten * make a backup if root is writable. 27789444c26fSTom Whitten */ 27799444c26fSTom Whitten if (r != BACKEND_CREATE_NEED_INIT && 27809444c26fSTom Whitten backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 27819444c26fSTom Whitten REP_PROTOCOL_SUCCESS) { 27829444c26fSTom Whitten struct stat sb; 27839444c26fSTom Whitten 27849444c26fSTom Whitten if ((stat(REPOSITORY_CHECKPOINT, &sb) == 0) && 27859444c26fSTom Whitten (sb.st_size > 0) && (sb.st_mode & S_IFREG)) { 27869444c26fSTom Whitten be->be_checkpoint = REPOSITORY_CHECKPOINT; 27879444c26fSTom Whitten flight_recorder_event( 27889444c26fSTom Whitten BE_FLIGHT_EV_CHECKPOINT_EXISTS, 27899444c26fSTom Whitten BE_FLIGHT_ST_INFO); 27909444c26fSTom Whitten } 27919444c26fSTom Whitten 27929444c26fSTom Whitten /* 27939444c26fSTom Whitten * If we have a checkpoint and root is writable, 27949444c26fSTom Whitten * make the backup now. 27959444c26fSTom Whitten */ 27969444c26fSTom Whitten if (be->be_checkpoint && writable_persist) { 27979444c26fSTom Whitten if (backend_create_backup_locked(be, 27989444c26fSTom Whitten REPOSITORY_BOOT_BACKUP) != 27999444c26fSTom Whitten REP_PROTOCOL_SUCCESS) { 28009444c26fSTom Whitten configd_critical( 28019444c26fSTom Whitten "unable to create \"%s\" backup of " 28029444c26fSTom Whitten "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 28039444c26fSTom Whitten be->be_path); 28049444c26fSTom Whitten } 28059444c26fSTom Whitten } 28069444c26fSTom Whitten backend_unlock(be); 28079444c26fSTom Whitten } 28087c478bd9Sstevel@tonic-gate } 28098bcc75f2Samaguire 28108bcc75f2Samaguire /* 28118bcc75f2Samaguire * If the persistent backend is writable at this point, upgrade it. 28128bcc75f2Samaguire * This can occur in a few cases, most notably on UFS roots if 28138bcc75f2Samaguire * we are operating on the backend from another root, as is the case 28148bcc75f2Samaguire * during alternate-root BFU. 28158bcc75f2Samaguire * 28168bcc75f2Samaguire * Otherwise, upgrade will occur via backend_check_readonly() when 28178bcc75f2Samaguire * the repository is re-opened read-write. 28188bcc75f2Samaguire */ 28198bcc75f2Samaguire if (writable_persist) { 28208bcc75f2Samaguire r = backend_lock(BACKEND_TYPE_NORMAL, 1, &be); 28218bcc75f2Samaguire assert(r == REP_PROTOCOL_SUCCESS); 28228bcc75f2Samaguire backend_check_upgrade(be, B_TRUE); 28238bcc75f2Samaguire backend_unlock(be); 28248bcc75f2Samaguire } 28258bcc75f2Samaguire 28267c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_OKAY); 28277c478bd9Sstevel@tonic-gate } 28287c478bd9Sstevel@tonic-gate 28297c478bd9Sstevel@tonic-gate /* 28307c478bd9Sstevel@tonic-gate * quiesce all database activity prior to exiting 28317c478bd9Sstevel@tonic-gate */ 28327c478bd9Sstevel@tonic-gate void 28337c478bd9Sstevel@tonic-gate backend_fini(void) 28347c478bd9Sstevel@tonic-gate { 28357c478bd9Sstevel@tonic-gate sqlite_backend_t *be_normal, *be_np; 28367c478bd9Sstevel@tonic-gate 28377c478bd9Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NORMAL, 1, &be_normal); 28387c478bd9Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NONPERSIST, 1, &be_np); 28397c478bd9Sstevel@tonic-gate } 28407c478bd9Sstevel@tonic-gate 28417c478bd9Sstevel@tonic-gate #define QUERY_BASE 128 28427c478bd9Sstevel@tonic-gate backend_query_t * 28437c478bd9Sstevel@tonic-gate backend_query_alloc(void) 28447c478bd9Sstevel@tonic-gate { 28457c478bd9Sstevel@tonic-gate backend_query_t *q; 28467c478bd9Sstevel@tonic-gate q = calloc(1, sizeof (backend_query_t)); 28477c478bd9Sstevel@tonic-gate if (q != NULL) { 28487c478bd9Sstevel@tonic-gate q->bq_size = QUERY_BASE; 28497c478bd9Sstevel@tonic-gate q->bq_buf = calloc(1, q->bq_size); 28507c478bd9Sstevel@tonic-gate if (q->bq_buf == NULL) { 28517c478bd9Sstevel@tonic-gate q->bq_size = 0; 28527c478bd9Sstevel@tonic-gate } 28537c478bd9Sstevel@tonic-gate 28547c478bd9Sstevel@tonic-gate } 28557c478bd9Sstevel@tonic-gate return (q); 28567c478bd9Sstevel@tonic-gate } 28577c478bd9Sstevel@tonic-gate 28587c478bd9Sstevel@tonic-gate void 28597c478bd9Sstevel@tonic-gate backend_query_append(backend_query_t *q, const char *value) 28607c478bd9Sstevel@tonic-gate { 28617c478bd9Sstevel@tonic-gate char *alloc; 28627c478bd9Sstevel@tonic-gate int count; 28637c478bd9Sstevel@tonic-gate size_t size, old_len; 28647c478bd9Sstevel@tonic-gate 28657c478bd9Sstevel@tonic-gate if (q == NULL) { 28667c478bd9Sstevel@tonic-gate /* We'll discover the error when we try to run the query. */ 28677c478bd9Sstevel@tonic-gate return; 28687c478bd9Sstevel@tonic-gate } 28697c478bd9Sstevel@tonic-gate 28707c478bd9Sstevel@tonic-gate while (q->bq_buf != NULL) { 28717c478bd9Sstevel@tonic-gate old_len = strlen(q->bq_buf); 28727c478bd9Sstevel@tonic-gate size = q->bq_size; 28737c478bd9Sstevel@tonic-gate count = strlcat(q->bq_buf, value, size); 28747c478bd9Sstevel@tonic-gate 28757c478bd9Sstevel@tonic-gate if (count < size) 28767c478bd9Sstevel@tonic-gate break; /* success */ 28777c478bd9Sstevel@tonic-gate 28787c478bd9Sstevel@tonic-gate q->bq_buf[old_len] = 0; 28797c478bd9Sstevel@tonic-gate size = round_up_to_p2(count + 1); 28807c478bd9Sstevel@tonic-gate 28817c478bd9Sstevel@tonic-gate assert(size > q->bq_size); 28827c478bd9Sstevel@tonic-gate alloc = realloc(q->bq_buf, size); 28837c478bd9Sstevel@tonic-gate if (alloc == NULL) { 28847c478bd9Sstevel@tonic-gate free(q->bq_buf); 28857c478bd9Sstevel@tonic-gate q->bq_buf = NULL; 28867c478bd9Sstevel@tonic-gate break; /* can't grow */ 28877c478bd9Sstevel@tonic-gate } 28887c478bd9Sstevel@tonic-gate 28897c478bd9Sstevel@tonic-gate q->bq_buf = alloc; 28907c478bd9Sstevel@tonic-gate q->bq_size = size; 28917c478bd9Sstevel@tonic-gate } 28927c478bd9Sstevel@tonic-gate } 28937c478bd9Sstevel@tonic-gate 28947c478bd9Sstevel@tonic-gate void 28957c478bd9Sstevel@tonic-gate backend_query_add(backend_query_t *q, const char *format, ...) 28967c478bd9Sstevel@tonic-gate { 28977c478bd9Sstevel@tonic-gate va_list args; 28987c478bd9Sstevel@tonic-gate char *new; 28997c478bd9Sstevel@tonic-gate 29007c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 29017c478bd9Sstevel@tonic-gate return; 29027c478bd9Sstevel@tonic-gate 29037c478bd9Sstevel@tonic-gate va_start(args, format); 29047c478bd9Sstevel@tonic-gate new = sqlite_vmprintf(format, args); 29057c478bd9Sstevel@tonic-gate va_end(args); 29067c478bd9Sstevel@tonic-gate 29077c478bd9Sstevel@tonic-gate if (new == NULL) { 29087c478bd9Sstevel@tonic-gate free(q->bq_buf); 29097c478bd9Sstevel@tonic-gate q->bq_buf = NULL; 29107c478bd9Sstevel@tonic-gate return; 29117c478bd9Sstevel@tonic-gate } 29127c478bd9Sstevel@tonic-gate 29137c478bd9Sstevel@tonic-gate backend_query_append(q, new); 29147c478bd9Sstevel@tonic-gate 29157c478bd9Sstevel@tonic-gate free(new); 29167c478bd9Sstevel@tonic-gate } 29177c478bd9Sstevel@tonic-gate 29187c478bd9Sstevel@tonic-gate void 29197c478bd9Sstevel@tonic-gate backend_query_free(backend_query_t *q) 29207c478bd9Sstevel@tonic-gate { 29217c478bd9Sstevel@tonic-gate if (q != NULL) { 29227c478bd9Sstevel@tonic-gate if (q->bq_buf != NULL) { 29237c478bd9Sstevel@tonic-gate free(q->bq_buf); 29247c478bd9Sstevel@tonic-gate } 29257c478bd9Sstevel@tonic-gate free(q); 29267c478bd9Sstevel@tonic-gate } 29277c478bd9Sstevel@tonic-gate } 2928