1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2001,2008 Oracle.  All rights reserved.
5 *
6 * $Id: rep_mgr.c,v 12.22 2008/01/08 20:58:25 bostic Exp $
7 */
8
9#include <sys/types.h>
10#include <signal.h>
11#include <stdlib.h>
12#include <string.h>
13#ifndef _WIN32
14#include <unistd.h>
15#endif
16
17#include <db.h>
18
19#include "../common/rep_common.h"
20
21typedef struct {
22	SHARED_DATA shared_data;
23} APP_DATA;
24
25const char *progname = "ex_rep";
26
27#ifdef _WIN32
28extern int getopt(int, char * const *, const char *);
29#endif
30
31static void event_callback __P((DB_ENV *, u_int32_t, void *));
32
33int
34main(argc, argv)
35	int argc;
36	char *argv[];
37{
38	extern char *optarg;
39	DB_ENV *dbenv;
40	const char *home;
41	char ch, *host, *portstr;
42	int ret, totalsites, t_ret, got_listen_address, friend;
43	u_int16_t port;
44	APP_DATA my_app_data;
45	u_int32_t start_policy;
46	int priority;
47
48	my_app_data.shared_data.is_master = 0; /* assume start out as client */
49	dbenv = NULL;
50	ret = got_listen_address = 0;
51	home = "TESTDIR";
52
53	if ((ret = create_env(progname, &dbenv)) != 0)
54		goto err;
55	dbenv->app_private = &my_app_data;
56	(void)dbenv->set_event_notify(dbenv, event_callback);
57
58	start_policy = DB_REP_ELECTION;	/* default */
59	priority = 100;		/* default */
60
61	while ((ch = getopt(argc, argv, "Cf:h:Mm:n:o:p:v")) != EOF) {
62		friend = 0;
63		switch (ch) {
64		case 'C':
65			start_policy = DB_REP_CLIENT;
66			break;
67		case 'h':
68			home = optarg;
69			break;
70		case 'M':
71			start_policy = DB_REP_MASTER;
72			break;
73		case 'm':
74			host = strtok(optarg, ":");
75			if ((portstr = strtok(NULL, ":")) == NULL) {
76				fprintf(stderr, "Bad host specification.\n");
77				goto err;
78			}
79			port = (unsigned short)atoi(portstr);
80			if ((ret = dbenv->repmgr_set_local_site(dbenv,
81			    host, port, 0)) != 0) {
82				fprintf(stderr,
83				    "Could not set listen address (%d).\n",
84				    ret);
85				goto err;
86			}
87			got_listen_address = 1;
88			break;
89		case 'n':
90			totalsites = atoi(optarg);
91			if ((ret =
92			    dbenv->rep_set_nsites(dbenv, totalsites)) != 0)
93				dbenv->err(dbenv, ret, "set_nsites");
94			break;
95		case 'f':
96			friend = 1; /* FALLTHROUGH */
97		case 'o':
98			host = strtok(optarg, ":");
99			if ((portstr = strtok(NULL, ":")) == NULL) {
100				fprintf(stderr, "Bad host specification.\n");
101				goto err;
102			}
103			port = (unsigned short)atoi(portstr);
104			if ((ret = dbenv->repmgr_add_remote_site(dbenv, host,
105			    port, NULL, friend ? DB_REPMGR_PEER : 0)) != 0) {
106				dbenv->err(dbenv, ret,
107				    "Could not add site %s:%d", host,
108				    (int)port);
109				goto err;
110			}
111			break;
112		case 'p':
113			priority = atoi(optarg);
114			break;
115		case 'v':
116			if ((ret = dbenv->set_verbose(dbenv,
117			    DB_VERB_REPLICATION, 1)) != 0)
118				goto err;
119			break;
120		case '?':
121		default:
122			usage(progname);
123		}
124	}
125
126	/* Error check command line. */
127	if ((!got_listen_address) || home == NULL)
128		usage(progname);
129
130	dbenv->rep_set_priority(dbenv, priority);
131
132	if ((ret = env_init(dbenv, home)) != 0)
133		goto err;
134
135	if ((ret = dbenv->repmgr_start(dbenv, 3, start_policy)) != 0)
136		goto err;
137
138	if ((ret = doloop(dbenv, &my_app_data.shared_data)) != 0) {
139		dbenv->err(dbenv, ret, "Client failed");
140		goto err;
141	}
142
143	/*
144	 * We have used the DB_TXN_NOSYNC environment flag for improved
145	 * performance without the usual sacrifice of transactional durability,
146	 * as discussed in the "Transactional guarantees" page of the Reference
147	 * Guide: if one replication site crashes, we can expect the data to
148	 * exist at another site.  However, in case we shut down all sites
149	 * gracefully, we push out the end of the log here so that the most
150	 * recent transactions don't mysteriously disappear.
151	 */
152	if ((ret = dbenv->log_flush(dbenv, NULL)) != 0) {
153		dbenv->err(dbenv, ret, "log_flush");
154		goto err;
155	}
156
157err:
158	if (dbenv != NULL &&
159	    (t_ret = dbenv->close(dbenv, 0)) != 0) {
160		fprintf(stderr, "failure closing env: %s (%d)\n",
161		    db_strerror(t_ret), t_ret);
162		if (ret == 0)
163			ret = t_ret;
164	}
165
166	return (ret);
167}
168
169static void
170event_callback(dbenv, which, info)
171	DB_ENV *dbenv;
172	u_int32_t which;
173	void *info;
174{
175	APP_DATA *app = dbenv->app_private;
176	SHARED_DATA *shared = &app->shared_data;
177
178	info = NULL;				/* Currently unused. */
179
180	switch (which) {
181	case DB_EVENT_REP_CLIENT:
182		shared->is_master = 0;
183		break;
184
185	case DB_EVENT_REP_MASTER:
186		shared->is_master = 1;
187		break;
188
189	case DB_EVENT_REP_PERM_FAILED:
190		printf("insufficient acks\n");
191		break;
192
193	case DB_EVENT_REP_STARTUPDONE: /* FALLTHROUGH */
194	case DB_EVENT_REP_NEWMASTER:
195		/* I don't care about these, for now. */
196		break;
197
198	default:
199		dbenv->errx(dbenv, "ignoring event %d", which);
200	}
201}
202