1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_deadlock.c,v 12.22 2008/01/08 20:58:13 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
18int main __P((int, char *[]));
19int usage __P((void));
20int version_check __P((void));
21
22const char *progname;
23
24int
25main(argc, argv)
26	int argc;
27	char *argv[];
28{
29	extern char *optarg;
30	extern int optind;
31	DB_ENV *dbenv;
32	u_int32_t atype;
33	time_t now;
34	u_long secs, usecs;
35	int rejected, ch, exitval, ret, verbose;
36	char *home, *logfile, *passwd, *str, time_buf[CTIME_BUFLEN];
37
38	if ((progname = __db_rpath(argv[0])) == NULL)
39		progname = argv[0];
40	else
41		++progname;
42
43	if ((ret = version_check()) != 0)
44		return (ret);
45
46	dbenv = NULL;
47	atype = DB_LOCK_DEFAULT;
48	home = logfile = passwd = NULL;
49	secs = usecs = 0;
50	exitval = verbose = 0;
51	while ((ch = getopt(argc, argv, "a:h:L:P:t:Vv")) != EOF)
52		switch (ch) {
53		case 'a':
54			switch (optarg[0]) {
55			case 'e':
56				atype = DB_LOCK_EXPIRE;
57				break;
58			case 'm':
59				atype = DB_LOCK_MAXLOCKS;
60				break;
61			case 'n':
62				atype = DB_LOCK_MINLOCKS;
63				break;
64			case 'o':
65				atype = DB_LOCK_OLDEST;
66				break;
67			case 'W':
68				atype = DB_LOCK_MAXWRITE;
69				break;
70			case 'w':
71				atype = DB_LOCK_MINWRITE;
72				break;
73			case 'y':
74				atype = DB_LOCK_YOUNGEST;
75				break;
76			default:
77				return (usage());
78				/* NOTREACHED */
79			}
80			if (optarg[1] != '\0')
81				return (usage());
82			break;
83		case 'h':
84			home = optarg;
85			break;
86		case 'L':
87			logfile = optarg;
88			break;
89		case 'P':
90			passwd = strdup(optarg);
91			memset(optarg, 0, strlen(optarg));
92			if (passwd == NULL) {
93				fprintf(stderr, "%s: strdup: %s\n",
94				    progname, strerror(errno));
95				return (EXIT_FAILURE);
96			}
97			break;
98		case 't':
99			if ((str = strchr(optarg, '.')) != NULL) {
100				*str++ = '\0';
101				if (*str != '\0' && __db_getulong(
102				    NULL, progname, str, 0, LONG_MAX, &usecs))
103					return (EXIT_FAILURE);
104			}
105			if (*optarg != '\0' && __db_getulong(
106			    NULL, progname, optarg, 0, LONG_MAX, &secs))
107				return (EXIT_FAILURE);
108			if (secs == 0 && usecs == 0)
109				return (usage());
110
111			break;
112		case 'V':
113			printf("%s\n", db_version(NULL, NULL, NULL));
114			return (EXIT_SUCCESS);
115		case 'v':
116			verbose = 1;
117			break;
118		case '?':
119		default:
120			return (usage());
121		}
122	argc -= optind;
123	argv += optind;
124
125	if (argc != 0)
126		return (usage());
127
128	/* Handle possible interruptions. */
129	__db_util_siginit();
130
131	/* Log our process ID. */
132	if (logfile != NULL && __db_util_logset(progname, logfile))
133		goto shutdown;
134
135	/*
136	 * Create an environment object and initialize it for error
137	 * reporting.
138	 */
139	if ((ret = db_env_create(&dbenv, 0)) != 0) {
140		fprintf(stderr,
141		    "%s: db_env_create: %s\n", progname, db_strerror(ret));
142		goto shutdown;
143	}
144
145	dbenv->set_errfile(dbenv, stderr);
146	dbenv->set_errpfx(dbenv, progname);
147
148	if (passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
149	    passwd, DB_ENCRYPT_AES)) != 0) {
150		dbenv->err(dbenv, ret, "set_passwd");
151		goto shutdown;
152	}
153
154	if (verbose) {
155		(void)dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK, 1);
156		(void)dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR, 1);
157	}
158
159	/* An environment is required. */
160	if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) != 0) {
161		dbenv->err(dbenv, ret, "open");
162		goto shutdown;
163	}
164
165	while (!__db_util_interrupted()) {
166		if (verbose) {
167			(void)time(&now);
168			dbenv->errx(dbenv,
169			    "running at %.24s", __os_ctime(&now, time_buf));
170		}
171
172		if ((ret =
173		    dbenv->lock_detect(dbenv, 0, atype, &rejected)) != 0) {
174			dbenv->err(dbenv, ret, "DB_ENV->lock_detect");
175			goto shutdown;
176		}
177		if (verbose)
178			dbenv->errx(dbenv, "rejected %d locks", rejected);
179
180		/* Make a pass every "secs" secs and "usecs" usecs. */
181		if (secs == 0 && usecs == 0)
182			break;
183		__os_yield(dbenv->env, secs, usecs);
184	}
185
186	if (0) {
187shutdown:	exitval = 1;
188	}
189
190	/* Clean up the logfile. */
191	if (logfile != NULL)
192		(void)remove(logfile);
193
194	/* Clean up the environment. */
195	if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
196		exitval = 1;
197		fprintf(stderr,
198		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
199	}
200
201	if (passwd != NULL)
202		free(passwd);
203
204	/* Resend any caught signal. */
205	__db_util_sigresend();
206
207	return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
208}
209
210int
211usage()
212{
213	(void)fprintf(stderr,
214	    "usage: %s [-Vv] [-a e | m | n | o | W | w | y]\n\t%s\n", progname,
215	    "[-h home] [-L file] [-P password] [-t sec.usec]");
216	return (EXIT_FAILURE);
217}
218
219int
220version_check()
221{
222	int v_major, v_minor, v_patch;
223
224	/* Make sure we're loaded with the right version of the DB library. */
225	(void)db_version(&v_major, &v_minor, &v_patch);
226	if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
227		fprintf(stderr,
228	"%s: version %d.%d doesn't match library version %d.%d\n",
229		    progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
230		    v_major, v_minor);
231		return (EXIT_FAILURE);
232	}
233	return (0);
234}
235