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