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