1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#include <sys/types.h>
10
11#include <stdlib.h>
12#include <string.h>
13
14#ifdef _WIN32
15extern int getopt(int, char * const *, const char *);
16#else
17#include <unistd.h>
18#endif
19
20#include <db.h>
21
22int db_init __P((const char *, u_int32_t, int));
23int main __P((int, char *[]));
24int usage __P((void));
25
26DB_ENV	 *dbenv;
27const char
28	*progname = "ex_lock";				/* Program name. */
29
30int
31main(argc, argv)
32	int argc;
33	char *argv[];
34{
35	extern char *optarg;
36	extern int optind;
37	DBT lock_dbt;
38	DB_LOCK lock;
39	DB_LOCK *locks;
40	db_lockmode_t lock_type;
41	long held;
42	size_t len;
43	u_int32_t locker, maxlocks;
44	int ch, do_unlink, did_get, i, lockid, lockcount, ret;
45	const char *home;
46	char opbuf[16], objbuf[1024], lockbuf[16];
47
48	home = "TESTDIR";
49	maxlocks = 0;
50	do_unlink = 0;
51	while ((ch = getopt(argc, argv, "h:m:u")) != EOF)
52		switch (ch) {
53		case 'h':
54			home = optarg;
55			break;
56		case 'm':
57			if ((i = atoi(optarg)) <= 0)
58				return (usage());
59			maxlocks = (u_int32_t)i;  /* XXX: possible overflow. */
60			break;
61		case 'u':
62			do_unlink = 1;
63			break;
64		case '?':
65		default:
66			return (usage());
67		}
68	argc -= optind;
69	argv += optind;
70
71	if (argc != 0)
72		return (usage());
73
74	/* Initialize the database environment. */
75	if ((ret = db_init(home, maxlocks, do_unlink)) != 0)
76		return (ret);
77
78	locks = 0;
79	lockcount = 0;
80
81	/*
82	 * Accept lock requests.
83	 */
84	if ((ret = dbenv->lock_id(dbenv, &locker)) != 0) {
85		dbenv->err(dbenv, ret, "unable to get locker id");
86		(void)dbenv->close(dbenv, 0);
87		return (EXIT_FAILURE);
88	}
89	lockid = -1;
90
91	memset(&lock_dbt, 0, sizeof(lock_dbt));
92	for (held = 0, did_get = 0;;) {
93		printf("Operation get/release [get]> ");
94		fflush(stdout);
95		if (fgets(opbuf, sizeof(opbuf), stdin) == NULL)
96			break;
97		if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get\n") == 0) {
98			/* Acquire a lock. */
99			printf("input object (text string) to lock> ");
100			fflush(stdout);
101			if (fgets(objbuf, sizeof(objbuf), stdin) == NULL)
102				break;
103			if ((len = strlen(objbuf)) <= 1)
104				continue;
105
106			do {
107				printf("lock type read/write [read]> ");
108				fflush(stdout);
109				if (fgets(lockbuf,
110				    sizeof(lockbuf), stdin) == NULL)
111					break;
112				len = strlen(lockbuf);
113			} while (len > 1 &&
114			    strcmp(lockbuf, "read\n") != 0 &&
115			    strcmp(lockbuf, "write\n") != 0);
116			if (len == 1 || strcmp(lockbuf, "read\n") == 0)
117				lock_type = DB_LOCK_READ;
118			else
119				lock_type = DB_LOCK_WRITE;
120
121			lock_dbt.data = objbuf;
122			lock_dbt.size = (u_int32_t)strlen(objbuf);
123			ret = dbenv->lock_get(dbenv, locker,
124			    DB_LOCK_NOWAIT, &lock_dbt, lock_type, &lock);
125			if (ret == 0) {
126				did_get = 1;
127				lockid = lockcount++;
128				if (locks == NULL)
129					locks =
130					    (DB_LOCK *)malloc(sizeof(DB_LOCK));
131				else
132					locks = (DB_LOCK *)realloc(locks,
133					    lockcount * sizeof(DB_LOCK));
134				locks[lockid] = lock;
135			}
136		} else {
137			/* Release a lock. */
138			do {
139				printf("input lock to release> ");
140				fflush(stdout);
141				if (fgets(objbuf,
142				    sizeof(objbuf), stdin) == NULL)
143					break;
144			} while ((len = strlen(objbuf)) <= 1);
145			lockid = strtol(objbuf, NULL, 16);
146			if (lockid < 0 || lockid >= lockcount) {
147				printf("Lock #%d out of range\n", lockid);
148				continue;
149			}
150			lock = locks[lockid];
151			ret = dbenv->lock_put(dbenv, &lock);
152			did_get = 0;
153		}
154		switch (ret) {
155		case 0:
156			printf("Lock #%d %s\n", lockid,
157			    did_get ? "granted" : "released");
158			held += did_get ? 1 : -1;
159			break;
160		case DB_LOCK_NOTGRANTED:
161			dbenv->err(dbenv, ret, NULL);
162			break;
163		case DB_LOCK_DEADLOCK:
164			dbenv->err(dbenv, ret,
165			    "lock_%s", did_get ? "get" : "put");
166			break;
167		default:
168			dbenv->err(dbenv, ret,
169			    "lock_%s", did_get ? "get" : "put");
170			(void)dbenv->close(dbenv, 0);
171			return (EXIT_FAILURE);
172		}
173	}
174
175	printf("\nClosing lock region %ld locks held\n", held);
176
177	if (locks != NULL)
178		free(locks);
179
180	if ((ret = dbenv->close(dbenv, 0)) != 0) {
181		fprintf(stderr,
182		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
183		return (EXIT_FAILURE);
184	}
185	return (EXIT_SUCCESS);
186}
187
188/*
189 * db_init --
190 *	Initialize the environment.
191 */
192int
193db_init(home, maxlocks, do_unlink)
194	const char *home;
195	u_int32_t maxlocks;
196	int do_unlink;
197{
198	int ret;
199
200	if ((ret = db_env_create(&dbenv, 0)) != 0) {
201		fprintf(stderr, "%s: db_env_create: %s\n",
202		    progname, db_strerror(ret));
203		return (EXIT_FAILURE);
204	}
205
206	if (do_unlink) {
207		if ((ret = dbenv->remove(dbenv, home, DB_FORCE)) != 0) {
208			fprintf(stderr, "%s: dbenv->remove: %s\n",
209			    progname, db_strerror(ret));
210			return (EXIT_FAILURE);
211		}
212		if ((ret = db_env_create(&dbenv, 0)) != 0) {
213			fprintf(stderr, "%s: db_env_create: %s\n",
214			    progname, db_strerror(ret));
215			return (EXIT_FAILURE);
216		}
217	}
218
219	dbenv->set_errfile(dbenv, stderr);
220	dbenv->set_errpfx(dbenv, progname);
221	if (maxlocks != 0)
222		dbenv->set_lk_max_locks(dbenv, maxlocks);
223
224	if ((ret =
225	    dbenv->open(dbenv, home, DB_CREATE | DB_INIT_LOCK, 0)) != 0) {
226		dbenv->err(dbenv, ret, NULL);
227		(void)dbenv->close(dbenv, 0);
228		return (EXIT_FAILURE);
229	}
230	return (0);
231}
232
233int
234usage()
235{
236	(void)fprintf(stderr,
237	    "usage: %s [-u] [-h home] [-m maxlocks]\n", progname);
238	return (EXIT_FAILURE);
239}
240