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