1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2001, 2010 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
14static const char copyright[] =
15    "Copyright (c) 1996, 2010 Oracle and/or its affiliates.  All rights reserved.\n";
16#endif
17
18typedef enum { T_NOTSET, T_DB,
19    T_ENV, T_LOCK, T_LOG, T_MPOOL, T_MUTEX, T_REP, T_TXN } test_t;
20
21int	 db_init __P((DB_ENV *, char *, test_t, u_int32_t, int *));
22int	 main __P((int, char *[]));
23int	 usage __P((void));
24int	 version_check __P((void));
25
26const char *progname;
27
28int
29main(argc, argv)
30	int argc;
31	char *argv[];
32{
33	extern char *optarg;
34	extern int optind;
35	DB_ENV	*dbenv;
36	DB *dbp;
37	test_t ttype;
38	u_int32_t cache, flags;
39	int ch, exitval;
40	int nflag, private, resize, ret;
41	char *db, *home, *p, *passwd, *subdb;
42
43	if ((progname = __db_rpath(argv[0])) == NULL)
44		progname = argv[0];
45	else
46		++progname;
47
48	if ((ret = version_check()) != 0)
49		return (ret);
50
51	dbenv = NULL;
52	dbp = NULL;
53	ttype = T_NOTSET;
54	cache = MEGABYTE;
55	exitval = flags = nflag = private = 0;
56	db = home = passwd = subdb = NULL;
57
58	while ((ch = getopt(argc,
59	    argv, "C:cd:Eefgh:L:lM:mNP:R:rs:tVxX:Z")) != EOF)
60		switch (ch) {
61		case 'C': case 'c':
62			if (ttype != T_NOTSET && ttype != T_LOCK)
63				goto argcombo;
64			ttype = T_LOCK;
65			if (ch != 'c')
66				for (p = optarg; *p; ++p)
67					switch (*p) {
68					case 'A':
69						LF_SET(DB_STAT_ALL);
70						break;
71					case 'c':
72						LF_SET(DB_STAT_LOCK_CONF);
73						break;
74					case 'l':
75						LF_SET(DB_STAT_LOCK_LOCKERS);
76						break;
77					case 'm': /* Backward compatible. */
78						break;
79					case 'o':
80						LF_SET(DB_STAT_LOCK_OBJECTS);
81						break;
82					case 'p':
83						LF_SET(DB_STAT_LOCK_PARAMS);
84						break;
85					default:
86						return (usage());
87					}
88			break;
89		case 'd':
90			if (ttype != T_NOTSET && ttype != T_DB)
91				goto argcombo;
92			ttype = T_DB;
93			db = optarg;
94			break;
95		case 'E': case 'e':
96			if (ttype != T_NOTSET && ttype != T_ENV)
97				goto argcombo;
98			ttype = T_ENV;
99			LF_SET(DB_STAT_SUBSYSTEM);
100			if (ch == 'E')
101				LF_SET(DB_STAT_ALL);
102			break;
103		case 'f':
104			if (ttype != T_NOTSET && ttype != T_DB)
105				goto argcombo;
106			ttype = T_DB;
107			LF_SET(DB_FAST_STAT);
108			break;
109		case 'h':
110			home = optarg;
111			break;
112		case 'L': case 'l':
113			if (ttype != T_NOTSET && ttype != T_LOG)
114				goto argcombo;
115			ttype = T_LOG;
116			if (ch != 'l')
117				for (p = optarg; *p; ++p)
118					switch (*p) {
119					case 'A':
120						LF_SET(DB_STAT_ALL);
121						break;
122					default:
123						return (usage());
124					}
125			break;
126		case 'M': case 'm':
127			if (ttype != T_NOTSET && ttype != T_MPOOL)
128				goto argcombo;
129			ttype = T_MPOOL;
130			if (ch != 'm')
131				for (p = optarg; *p; ++p)
132					switch (*p) {
133					case 'A':
134						LF_SET(DB_STAT_ALL);
135						break;
136					case 'h':
137						LF_SET(DB_STAT_MEMP_HASH);
138						break;
139					case 'm': /* Backward compatible. */
140						break;
141					default:
142						return (usage());
143					}
144			break;
145		case 'N':
146			nflag = 1;
147			break;
148		case 'P':
149			passwd = strdup(optarg);
150			memset(optarg, 0, strlen(optarg));
151			if (passwd == NULL) {
152				fprintf(stderr, "%s: strdup: %s\n",
153				    progname, strerror(errno));
154				return (EXIT_FAILURE);
155			}
156			break;
157		case 'R': case 'r':
158			if (ttype != T_NOTSET && ttype != T_REP)
159				goto argcombo;
160			ttype = T_REP;
161			if (ch != 'r')
162				for (p = optarg; *p; ++p)
163					switch (*p) {
164					case 'A':
165						LF_SET(DB_STAT_ALL);
166						break;
167					default:
168						return (usage());
169					}
170			break;
171		case 's':
172			if (ttype != T_NOTSET && ttype != T_DB)
173				goto argcombo;
174			ttype = T_DB;
175			subdb = optarg;
176			break;
177		case 't':
178			if (ttype != T_NOTSET) {
179argcombo:			fprintf(stderr,
180				    "%s: illegal option combination\n",
181				    progname);
182				return (usage());
183			}
184			ttype = T_TXN;
185			break;
186		case 'V':
187			printf("%s\n", db_version(NULL, NULL, NULL));
188			return (EXIT_SUCCESS);
189		case 'X': case 'x':
190			if (ttype != T_NOTSET && ttype != T_MUTEX)
191				goto argcombo;
192			ttype = T_MUTEX;
193			if (ch != 'x')
194				for (p = optarg; *p; ++p)
195					switch (*p) {
196						case 'A':
197							LF_SET(DB_STAT_ALL);
198							break;
199						default:
200							return (usage());
201					}
202			break;
203		case 'Z':
204			LF_SET(DB_STAT_CLEAR);
205			break;
206		case '?':
207		default:
208			return (usage());
209		}
210	argc -= optind;
211	argv += optind;
212
213	switch (ttype) {
214	case T_DB:
215		if (db == NULL)
216			return (usage());
217		break;
218	case T_ENV:
219	case T_LOCK:
220	case T_LOG:
221	case T_MPOOL:
222	case T_MUTEX:
223	case T_REP:
224	case T_TXN:
225		break;
226	case T_NOTSET:
227		return (usage());
228	}
229
230	/* Handle possible interruptions. */
231	__db_util_siginit();
232
233	/*
234	 * Create an environment object and initialize it for error
235	 * reporting.
236	 */
237retry:	if ((ret = db_env_create(&dbenv, 0)) != 0) {
238		fprintf(stderr,
239		    "%s: db_env_create: %s\n", progname, db_strerror(ret));
240		goto err;
241	}
242
243	dbenv->set_errfile(dbenv, stderr);
244	dbenv->set_errpfx(dbenv, progname);
245
246	if (nflag) {
247		if ((ret = dbenv->set_flags(dbenv, DB_NOLOCKING, 1)) != 0) {
248			dbenv->err(dbenv, ret, "set_flags: DB_NOLOCKING");
249			goto err;
250		}
251		if ((ret = dbenv->set_flags(dbenv, DB_NOPANIC, 1)) != 0) {
252			dbenv->err(dbenv, ret, "set_flags: DB_NOPANIC");
253			goto err;
254		}
255	}
256
257	if (passwd != NULL &&
258	    (ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES)) != 0) {
259		dbenv->err(dbenv, ret, "set_passwd");
260		goto err;
261	}
262
263	/* Initialize the environment. */
264	if (db_init(dbenv, home, ttype, cache, &private) != 0)
265		goto err;
266
267	switch (ttype) {
268	case T_DB:
269		/* Create the DB object and open the file. */
270		if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
271			dbenv->err(dbenv, ret, "db_create");
272			goto err;
273		}
274
275		/*
276		 * We open the database for writing so we can update the cached
277		 * statistics, but it's OK to fail, we can open read-only and
278		 * proceed.
279		 *
280		 * Turn off error messages for now -- we can't open lots of
281		 * databases read-write (for example, master databases and
282		 * hash databases for which we don't know the hash function).
283		 */
284		dbenv->set_errfile(dbenv, NULL);
285		ret = dbp->open(dbp, NULL, db, subdb, DB_UNKNOWN, 0, 0);
286		dbenv->set_errfile(dbenv, stderr);
287		if (ret != 0) {
288			/* Handles cannot be reused after a failed DB->open. */
289			(void)dbp->close(dbp, 0);
290			if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
291				dbenv->err(dbenv, ret, "db_create");
292				goto err;
293			}
294
295		       	if ((ret = dbp->open(dbp,
296			    NULL, db, subdb, DB_UNKNOWN, DB_RDONLY, 0)) != 0) {
297				dbenv->err(dbenv, ret, "DB->open: %s", db);
298				goto err;
299			}
300		}
301
302		/* Check if cache is too small for this DB's pagesize. */
303		if (private) {
304			if ((ret = __db_util_cache(dbp, &cache, &resize)) != 0)
305				goto err;
306			if (resize) {
307				(void)dbp->close(dbp, DB_NOSYNC);
308				dbp = NULL;
309
310				(void)dbenv->close(dbenv, 0);
311				dbenv = NULL;
312				goto retry;
313			}
314		}
315
316		if (dbp->stat_print(dbp, flags))
317			goto err;
318		break;
319	case T_ENV:
320		if (dbenv->stat_print(dbenv, flags))
321			goto err;
322		break;
323	case T_LOCK:
324		if (dbenv->lock_stat_print(dbenv, flags))
325			goto err;
326		break;
327	case T_LOG:
328		if (dbenv->log_stat_print(dbenv, flags))
329			goto err;
330		break;
331	case T_MPOOL:
332		if (dbenv->memp_stat_print(dbenv, flags))
333			goto err;
334		break;
335	case T_MUTEX:
336		if (dbenv->mutex_stat_print(dbenv, flags))
337			goto err;
338		break;
339	case T_REP:
340#ifdef HAVE_REPLICATION_THREADS
341		if (dbenv->repmgr_stat_print(dbenv, flags))
342			goto err;
343#endif
344		if (dbenv->rep_stat_print(dbenv, flags))
345			goto err;
346		break;
347	case T_TXN:
348		if (dbenv->txn_stat_print(dbenv, flags))
349			goto err;
350		break;
351	case T_NOTSET:
352		dbenv->errx(dbenv, "Unknown statistics flag");
353		goto err;
354	}
355
356	if (0) {
357err:		exitval = 1;
358	}
359	if (dbp != NULL && (ret = dbp->close(dbp, DB_NOSYNC)) != 0) {
360		exitval = 1;
361		dbenv->err(dbenv, ret, "close");
362	}
363	if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
364		exitval = 1;
365		fprintf(stderr,
366		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
367	}
368
369	if (passwd != NULL)
370		free(passwd);
371
372	/* Resend any caught signal. */
373	__db_util_sigresend();
374
375	return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
376}
377
378/*
379 * db_init --
380 *	Initialize the environment.
381 */
382int
383db_init(dbenv, home, ttype, cache, is_private)
384	DB_ENV *dbenv;
385	char *home;
386	test_t ttype;
387	u_int32_t cache;
388	int *is_private;
389{
390	u_int32_t oflags;
391	int ret;
392
393	/*
394	 * If our environment open fails, and we're trying to look at a
395	 * shared region, it's a hard failure.
396	 *
397	 * We will probably just drop core if the environment we join does
398	 * not include a memory pool.  This is probably acceptable; trying
399	 * to use an existing environment that does not contain a memory
400	 * pool to look at a database can be safely construed as operator
401	 * error, I think.
402	 */
403	*is_private = 0;
404	if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) == 0)
405		return (0);
406	if (ret == DB_VERSION_MISMATCH)
407		goto err;
408	if (ttype != T_DB && ttype != T_LOG) {
409		dbenv->err(dbenv, ret, "DB_ENV->open%s%s",
410		    home == NULL ? "" : ": ", home == NULL ? "" : home);
411		return (1);
412	}
413
414	/*
415	 * We're looking at a database or set of log files and no environment
416	 * exists.  Create one, but make it private so no files are actually
417	 * created.  Declare a reasonably large cache so that we don't fail
418	 * when reporting statistics on large databases.
419	 *
420	 * An environment is required to look at databases because we may be
421	 * trying to look at databases in directories other than the current
422	 * one.
423	 */
424	if ((ret = dbenv->set_cachesize(dbenv, 0, cache, 1)) != 0) {
425		dbenv->err(dbenv, ret, "set_cachesize");
426		return (1);
427	}
428	*is_private = 1;
429	oflags = DB_CREATE | DB_PRIVATE | DB_USE_ENVIRON;
430	if (ttype == T_DB)
431		oflags |= DB_INIT_MPOOL;
432	if (ttype == T_LOG)
433		oflags |= DB_INIT_LOG;
434	if ((ret = dbenv->open(dbenv, home, oflags, 0)) == 0)
435		return (0);
436
437	/* An environment is required. */
438err:	dbenv->err(dbenv, ret, "DB_ENV->open");
439	return (1);
440}
441
442int
443usage()
444{
445	fprintf(stderr, "usage: %s %s\n", progname,
446	    "-d file [-fN] [-h home] [-P password] [-s database]");
447	fprintf(stderr, "usage: %s %s\n\t%s\n", progname,
448	    "[-cEelmNrtVxZ] [-C Aclop]",
449	    "[-h home] [-L A] [-M A] [-P password] [-R A] [-X A]");
450	return (EXIT_FAILURE);
451}
452
453int
454version_check()
455{
456	int v_major, v_minor, v_patch;
457
458	/* Make sure we're loaded with the right version of the DB library. */
459	(void)db_version(&v_major, &v_minor, &v_patch);
460	if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
461		fprintf(stderr,
462	"%s: version %d.%d doesn't match library version %d.%d\n",
463		    progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
464		    v_major, v_minor);
465		return (EXIT_FAILURE);
466	}
467	return (0);
468}
469