1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2006,2008 Oracle.  All rights reserved.
5 *
6 * $Id: rep_common.c,v 12.23 2008/04/04 21:16:36 alanb Exp $
7 */
8
9#include <errno.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <db.h>
14
15#include "rep_common.h"
16
17#define	CACHESIZE	(10 * 1024 * 1024)
18#define	DATABASE	"quote.db"
19#define	SLEEPTIME	3
20
21#ifdef _WIN32
22#define	WIN32_LEAN_AND_MEAN
23#include <windows.h>
24#define	sleep(s)		Sleep(1000 * (s))
25#endif
26
27static int print_stocks __P((DB *));
28
29static int
30print_stocks(dbp)
31	DB *dbp;
32{
33	DBC *dbc;
34	DBT key, data;
35#define	MAXKEYSIZE	10
36#define	MAXDATASIZE	20
37	char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];
38	int ret, t_ret;
39	u_int32_t keysize, datasize;
40
41	if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
42		dbp->err(dbp, ret, "can't open cursor");
43		return (ret);
44	}
45
46	memset(&key, 0, sizeof(key));
47	memset(&data, 0, sizeof(data));
48
49	printf("\tSymbol\tPrice\n");
50	printf("\t======\t=====\n");
51
52	for (ret = dbc->get(dbc, &key, &data, DB_FIRST);
53	    ret == 0;
54	    ret = dbc->get(dbc, &key, &data, DB_NEXT)) {
55		keysize = key.size > MAXKEYSIZE ? MAXKEYSIZE : key.size;
56		memcpy(keybuf, key.data, keysize);
57		keybuf[keysize] = '\0';
58
59		datasize = data.size >= MAXDATASIZE ? MAXDATASIZE : data.size;
60		memcpy(databuf, data.data, datasize);
61		databuf[datasize] = '\0';
62
63		printf("\t%s\t%s\n", keybuf, databuf);
64	}
65	printf("\n");
66	fflush(stdout);
67
68	if ((t_ret = dbc->close(dbc)) != 0 && ret == 0)
69		ret = t_ret;
70
71	switch (ret) {
72	case 0:
73	case DB_NOTFOUND:
74	case DB_LOCK_DEADLOCK:
75		return (0);
76	default:
77		return (ret);
78	}
79}
80
81#define	BUFSIZE 1024
82
83int
84doloop(dbenv, shared_data)
85	DB_ENV *dbenv;
86	SHARED_DATA *shared_data;
87{
88	DB *dbp;
89	DBT key, data;
90	char buf[BUFSIZE], *first, *price;
91	u_int32_t flags;
92	int ret;
93
94	dbp = NULL;
95	ret = 0;
96	memset(&key, 0, sizeof(key));
97	memset(&data, 0, sizeof(data));
98
99	for (;;) {
100		printf("QUOTESERVER%s> ",
101		    shared_data->is_master ? "" : " (read-only)");
102		fflush(stdout);
103
104		if (fgets(buf, sizeof(buf), stdin) == NULL)
105			break;
106
107#define	DELIM " \t\n"
108		if ((first = strtok(&buf[0], DELIM)) == NULL) {
109			/* Blank input line. */
110			price = NULL;
111		} else if ((price = strtok(NULL, DELIM)) == NULL) {
112			/* Just one input token. */
113			if (strncmp(buf, "exit", 4) == 0 ||
114			    strncmp(buf, "quit", 4) == 0)
115				break;
116			dbenv->errx(dbenv, "Format: TICKER VALUE");
117			continue;
118		} else {
119			/* Normal two-token input line. */
120			if (first != NULL && !shared_data->is_master) {
121				dbenv->errx(dbenv, "Can't update at client");
122				continue;
123			}
124		}
125
126		if (dbp == NULL) {
127			if ((ret = db_create(&dbp, dbenv, 0)) != 0)
128				return (ret);
129
130			/* Set page size small so page allocation is cheap. */
131			if ((ret = dbp->set_pagesize(dbp, 512)) != 0)
132				goto err;
133
134			flags = DB_AUTO_COMMIT;
135			if (shared_data->is_master)
136				flags |= DB_CREATE;
137			if ((ret = dbp->open(dbp,
138			    NULL, DATABASE, NULL, DB_BTREE, flags, 0)) != 0) {
139				if (ret == ENOENT) {
140					printf(
141					  "No stock database yet available.\n");
142					if ((ret = dbp->close(dbp, 0)) != 0) {
143						dbenv->err(dbenv, ret,
144						    "DB->close");
145						goto err;
146					}
147					dbp = NULL;
148					continue;
149				}
150				if (ret == DB_REP_HANDLE_DEAD ||
151				    ret == DB_LOCK_DEADLOCK) {
152					dbenv->err(dbenv, ret,
153					    "please retry the operation");
154					dbp->close(dbp, DB_NOSYNC);
155					dbp = NULL;
156					continue;
157				}
158				dbenv->err(dbenv, ret, "DB->open");
159				goto err;
160			}
161		}
162
163		if (first == NULL)
164			switch ((ret = print_stocks(dbp))) {
165			case 0:
166				break;
167			case DB_REP_HANDLE_DEAD:
168				(void)dbp->close(dbp, DB_NOSYNC);
169				dbp = NULL;
170				break;
171			default:
172				dbp->err(dbp, ret, "Error traversing data");
173				goto err;
174			}
175		else {
176			key.data = first;
177			key.size = (u_int32_t)strlen(first);
178
179			data.data = price;
180			data.size = (u_int32_t)strlen(price);
181
182			if ((ret = dbp->put(dbp,
183				 NULL, &key, &data, DB_AUTO_COMMIT)) != 0) {
184				dbp->err(dbp, ret, "DB->put");
185				goto err;
186			}
187		}
188	}
189
190err:	if (dbp != NULL)
191		(void)dbp->close(dbp, DB_NOSYNC);
192
193	return (ret);
194}
195
196int
197create_env(progname, dbenvp)
198	const char *progname;
199	DB_ENV **dbenvp;
200{
201	DB_ENV *dbenv;
202	int ret;
203
204	if ((ret = db_env_create(&dbenv, 0)) != 0) {
205		fprintf(stderr, "can't create env handle: %s\n",
206		    db_strerror(ret));
207		return (ret);
208	}
209
210	dbenv->set_errfile(dbenv, stderr);
211	dbenv->set_errpfx(dbenv, progname);
212
213	*dbenvp = dbenv;
214	return (0);
215}
216
217/* Open and configure an environment. */
218int
219env_init(dbenv, home)
220	DB_ENV *dbenv;
221	const char *home;
222{
223	u_int32_t flags;
224	int ret;
225
226	(void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
227	(void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
228
229	flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
230	    DB_INIT_REP | DB_INIT_TXN | DB_RECOVER | DB_THREAD;
231	if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
232		dbenv->err(dbenv, ret, "can't open environment");
233	return (ret);
234}
235
236/*
237 * In this application, we specify all communication via the command line.  In
238 * a real application, we would expect that information about the other sites
239 * in the system would be maintained in some sort of configuration file.  The
240 * critical part of this interface is that we assume at startup that we can
241 * find out
242 *	1) what host/port we wish to listen on for connections,
243 *	2) a (possibly empty) list of other sites we should attempt to connect
244 *	to; and
245 *	3) what our Berkeley DB home environment is.
246 *
247 * These pieces of information are expressed by the following flags.
248 * -m host:port (required; m stands for me)
249 * -o host:port (optional; o stands for other; any number of these may be
250 *	specified)
251 * -h home directory
252 * -n nsites (optional; number of sites in replication group; defaults to 0
253 *	in which case we try to dynamically compute the number of sites in
254 *	the replication group)
255 * -p priority (optional: defaults to 100)
256 * -C or -M  start up as client or master (optional)
257 */
258void
259usage(progname)
260	const char *progname;
261{
262	fprintf(stderr, "usage: %s ", progname);
263	fprintf(stderr, "[-CM][-h home][-o host:port][-m host:port]%s",
264	    "[-n nsites][-p priority]\n");
265	exit(EXIT_FAILURE);
266}
267