1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_recover.c,v 12.16 2008/01/08 20:58:16 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13#ifndef lint
14static const char copyright[] =
15    "Copyright (c) 1996,2008 Oracle.  All rights reserved.\n";
16#endif
17
18void db_recover_feedback __P((DB_ENV *, int, int));
19int  db_recover_main __P((int, char *[]));
20int  db_recover_read_timestamp __P((char *, time_t *));
21int  db_recover_usage __P((void));
22int  db_recover_version_check __P((void));
23
24const char *progname;
25int newline_needed;
26
27int
28db_recover(args)
29	char *args;
30{
31	int argc;
32	char **argv;
33
34	__db_util_arg("db_recover", args, &argc, &argv);
35	return (db_recover_main(argc, argv) ? EXIT_FAILURE : EXIT_SUCCESS);
36}
37
38#include <stdio.h>
39#define	ERROR_RETURN	ERROR
40
41int
42db_recover_main(argc, argv)
43	int argc;
44	char *argv[];
45{
46	extern char *optarg;
47	extern int optind, __db_getopt_reset;
48	DB_ENV	*dbenv;
49	time_t timestamp;
50	u_int32_t flags;
51	int ch, exitval, fatal_recover, ret, retain_env, set_feedback, verbose;
52	char *home, *passwd;
53
54	if ((progname = __db_rpath(argv[0])) == NULL)
55		progname = argv[0];
56	else
57		++progname;
58
59	if ((ret = db_recover_version_check()) != 0)
60		return (ret);
61
62	home = passwd = NULL;
63	timestamp = 0;
64	exitval = fatal_recover = retain_env = set_feedback = verbose = 0;
65	__db_getopt_reset = 1;
66	while ((ch = getopt(argc, argv, "cefh:P:t:Vv")) != EOF)
67		switch (ch) {
68		case 'c':
69			fatal_recover = 1;
70			break;
71		case 'e':
72			retain_env = 1;
73			break;
74		case 'f':
75			set_feedback = 1;
76			break;
77		case 'h':
78			home = optarg;
79			break;
80		case 'P':
81			passwd = strdup(optarg);
82			memset(optarg, 0, strlen(optarg));
83			if (passwd == NULL) {
84				fprintf(stderr, "%s: strdup: %s\n",
85				    progname, strerror(errno));
86				return (EXIT_FAILURE);
87			}
88			break;
89		case 't':
90			if ((ret = db_recover_read_timestamp(optarg, &timestamp)) != 0)
91				return (ret);
92			break;
93		case 'V':
94			printf("%s\n", db_version(NULL, NULL, NULL));
95			return (EXIT_SUCCESS);
96		case 'v':
97			verbose = 1;
98			break;
99		case '?':
100		default:
101			return (db_recover_usage());
102		}
103	argc -= optind;
104	argv += optind;
105
106	if (argc != 0)
107		return (db_recover_usage());
108
109	/* Handle possible interruptions. */
110	__db_util_siginit();
111
112	/*
113	 * Create an environment object and initialize it for error
114	 * reporting.
115	 */
116	if ((ret = db_env_create(&dbenv, 0)) != 0) {
117		fprintf(stderr,
118		    "%s: db_env_create: %s\n", progname, db_strerror(ret));
119		return (EXIT_FAILURE);
120	}
121	dbenv->set_errfile(dbenv, stderr);
122	dbenv->set_errpfx(dbenv, progname);
123	if (set_feedback)
124		(void)dbenv->set_feedback(dbenv, db_recover_feedback);
125	if (verbose)
126		(void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1);
127	if (timestamp &&
128	    (ret = dbenv->set_tx_timestamp(dbenv, &timestamp)) != 0) {
129		dbenv->err(dbenv, ret, "DB_ENV->set_timestamp");
130		goto shutdown;
131	}
132
133	if (passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
134	    passwd, DB_ENCRYPT_AES)) != 0) {
135		dbenv->err(dbenv, ret, "set_passwd");
136		goto shutdown;
137	}
138
139	/*
140	 * Initialize the environment -- we don't actually do anything
141	 * else, that all that's needed to run recovery.
142	 *
143	 * Note that unless the caller specified the -e option, we use a
144	 * private environment, as we're about to create a region, and we
145	 * don't want to to leave it around.  If we leave the region around,
146	 * the application that should create it will simply join it instead,
147	 * and will then be running with incorrectly sized (and probably
148	 * terribly small) caches.  Applications that use -e should almost
149	 * certainly use DB_CONFIG files in the directory.
150	 */
151	flags = 0;
152	LF_SET(DB_CREATE | DB_INIT_LOG |
153	    DB_INIT_MPOOL | DB_INIT_TXN | DB_USE_ENVIRON);
154	LF_SET(fatal_recover ? DB_RECOVER_FATAL : DB_RECOVER);
155	LF_SET(retain_env ? DB_INIT_LOCK : DB_PRIVATE);
156	if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) {
157		dbenv->err(dbenv, ret, "DB_ENV->open");
158		goto shutdown;
159	}
160
161	if (0) {
162shutdown:	exitval = 1;
163	}
164
165	/* Flush to the next line of the output device. */
166	if (newline_needed)
167		printf("\n");
168
169	/* Clean up the environment. */
170	if ((ret = dbenv->close(dbenv, 0)) != 0) {
171		exitval = 1;
172		fprintf(stderr,
173		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
174	}
175	if (passwd != NULL)
176		free(passwd);
177
178	/* Resend any caught signal. */
179	__db_util_sigresend();
180
181	return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
182}
183
184/*
185 * db_recover_feedback --
186 *	Provide feedback on recovery progress.
187 */
188void
189db_recover_feedback(dbenv, opcode, percent)
190	DB_ENV *dbenv;
191	int opcode;
192	int percent;
193{
194	COMPQUIET(dbenv, NULL);
195
196	if (opcode == DB_RECOVER) {
197		printf("\rrecovery %d%% complete", percent);
198		(void)fflush(stdout);
199		newline_needed = 1;
200	}
201}
202
203#define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
204
205/*
206 * read_timestamp --
207 *	Convert a time argument to Epoch seconds.
208 *
209 * Copyright (c) 1993
210 *	The Regents of the University of California.  All rights reserved.
211 *
212 * Redistribution and use in source and binary forms, with or without
213 * modification, are permitted provided that the following conditions
214 * are met:
215 * 1. Redistributions of source code must retain the above copyright
216 *    notice, this list of conditions and the following disclaimer.
217 * 2. Redistributions in binary form must reproduce the above copyright
218 *    notice, this list of conditions and the following disclaimer in the
219 *    documentation and/or other materials provided with the distribution.
220 * 3. Neither the name of the University nor the names of its contributors
221 *    may be used to endorse or promote products derived from this software
222 *    without specific prior written permission.
223 *
224 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
225 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
227 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
228 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
229 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
230 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
234 * SUCH DAMAGE.
235 */
236int
237db_recover_read_timestamp(arg, timep)
238	char *arg;
239	time_t *timep;
240{
241	struct tm *t;
242	time_t now;
243	int yearset;
244	char *p;
245					/* Start with the current time. */
246	(void)time(&now);
247	if ((t = localtime(&now)) == NULL) {
248		fprintf(stderr,
249		    "%s: localtime: %s\n", progname, strerror(errno));
250		return (EXIT_FAILURE);
251	}
252					/* [[CC]YY]MMDDhhmm[.SS] */
253	if ((p = strchr(arg, '.')) == NULL)
254		t->tm_sec = 0;		/* Seconds defaults to 0. */
255	else {
256		if (strlen(p + 1) != 2)
257			goto terr;
258		*p++ = '\0';
259		t->tm_sec = ATOI2(p);
260	}
261
262	yearset = 0;
263	switch (strlen(arg)) {
264	case 12:			/* CCYYMMDDhhmm */
265		t->tm_year = ATOI2(arg);
266		t->tm_year *= 100;
267		yearset = 1;
268		/* FALLTHROUGH */
269	case 10:			/* YYMMDDhhmm */
270		if (yearset) {
271			yearset = ATOI2(arg);
272			t->tm_year += yearset;
273		} else {
274			yearset = ATOI2(arg);
275			if (yearset < 69)
276				t->tm_year = yearset + 2000;
277			else
278				t->tm_year = yearset + 1900;
279		}
280		t->tm_year -= 1900;	/* Convert to UNIX time. */
281		/* FALLTHROUGH */
282	case 8:				/* MMDDhhmm */
283		t->tm_mon = ATOI2(arg);
284		--t->tm_mon;		/* Convert from 01-12 to 00-11 */
285		t->tm_mday = ATOI2(arg);
286		t->tm_hour = ATOI2(arg);
287		t->tm_min = ATOI2(arg);
288		break;
289	default:
290		goto terr;
291	}
292
293	t->tm_isdst = -1;		/* Figure out DST. */
294
295	*timep = mktime(t);
296	if (*timep == -1) {
297terr:		fprintf(stderr,
298	"%s: out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]",
299		    progname);
300		return (EXIT_FAILURE);
301	}
302	return (0);
303}
304
305int
306db_recover_usage()
307{
308	(void)fprintf(stderr, "usage: %s %s\n", progname,
309	    "[-cefVv] [-h home] [-P password] [-t [[CC]YY]MMDDhhmm[.SS]]");
310	return (EXIT_FAILURE);
311}
312
313int
314db_recover_version_check()
315{
316	int v_major, v_minor, v_patch;
317
318	/* Make sure we're loaded with the right version of the DB library. */
319	(void)db_version(&v_major, &v_minor, &v_patch);
320	if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
321		fprintf(stderr,
322	"%s: version %d.%d doesn't match library version %d.%d\n",
323		    progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
324		    v_major, v_minor);
325		return (EXIT_FAILURE);
326	}
327	return (0);
328}
329