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