1 /*-
2 * Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file EXAMPLES-LICENSE for license information.
5 *
6 * $Id$
7 */
8
9 /*
10 * NOTE: This example is a simplified version of the rep_mgr.c
11 * example that can be found in the db/examples/c/ex_rep/mgr directory.
12 *
13 * This example is intended only as an aid in learning Replication Manager
14 * concepts. It is not complete in that many features are not exercised
15 * in it, nor are many error conditions properly handled.
16 *
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #ifdef _WIN32
23 #include <windows.h>
24 #define sleep(s) Sleep(1000 * (s))
25 #else /* !_WIN32 */
26 #include <unistd.h>
27 #endif
28
29 #include <db.h>
30
31 #ifdef _WIN32
32 extern int getopt(int, char * const *, const char *);
33 #endif
34
35 #define CACHESIZE (10 * 1024 * 1024)
36 #define DATABASE "quote.db"
37 #define SLEEPTIME 3
38
39 typedef struct {
40 int is_master;
41 } APP_DATA;
42
43 const char *progname = "ex_rep_gsg_repmgr";
44
45 int create_env(const char *, DB_ENV **);
46 int env_init(DB_ENV *, const char *);
47 int doloop (DB_ENV *);
48 int print_stocks(DB *);
49 static void event_callback(DB_ENV *, u_int32_t, void *);
50
51 /* Usage function */
52 static void
usage()53 usage()
54 {
55 fprintf(stderr, "usage: %s ", progname);
56 fprintf(stderr, "-h home -l|-L host:port\n");
57 fprintf(stderr, "\t\t[-r host:port][-p priority]\n");
58 fprintf(stderr, "where:\n");
59 fprintf(stderr, "\t-h identifies the environment home directory ");
60 fprintf(stderr, "(required).\n");
61 fprintf(stderr, "\t-l identifies the host and port used by this ");
62 fprintf(stderr, "site (required unless L is specified).\n");
63 fprintf(stderr, "\t-L identifies the local site as group creator. \n");
64 fprintf(stderr, "\t-r identifies another site participating in ");
65 fprintf(stderr, "this replication group\n");
66 fprintf(stderr, "\t-p identifies the election priority used by ");
67 fprintf(stderr, "this replica.\n");
68 exit(EXIT_FAILURE);
69 }
70
71 int
main(int argc,char * argv[])72 main(int argc, char *argv[])
73 {
74 DB_ENV *dbenv;
75 DB_SITE *dbsite;
76 extern char *optarg;
77 const char *home;
78 char ch, *host, *last_colon, *portstr;
79 int local_is_set, ret, is_group_creator;
80 u_int16_t port;
81 /* Used to track whether this is a replica or a master. */
82 APP_DATA my_app_data;
83
84 dbenv = NULL;
85 ret = local_is_set = is_group_creator = 0;
86 home = NULL;
87
88 my_app_data.is_master = 0; /* Assume that we start as a replica */
89
90 if ((ret = create_env(progname, &dbenv)) != 0)
91 goto err;
92
93 /* Make APP_DATA available through the environment handle. */
94 dbenv->app_private = &my_app_data;
95
96 /* Default priority is 100. */
97 dbenv->rep_set_priority(dbenv, 100);
98 /* Permanent messages require at least one ack. */
99 dbenv->repmgr_set_ack_policy(dbenv, DB_REPMGR_ACKS_ONE);
100 /* Give 500 microseconds to receive the ack. */
101 dbenv->rep_set_timeout(dbenv, DB_REP_ACK_TIMEOUT, 500);
102
103 /* Collect the command line options. */
104 while ((ch = getopt(argc, argv, "h:l:L:p:r:")) != EOF)
105 switch (ch) {
106 case 'h':
107 home = optarg;
108 break;
109 /* Set the host and port used by this environment. */
110 case 'L':
111 is_group_creator = 1; /* FALLTHROUGH */
112 case 'l':
113 host = optarg;
114 /*
115 * The final colon in host:port string is the
116 * boundary between the host and the port portions
117 * of the string.
118 */
119 if ((last_colon = strrchr(host, ':')) == NULL ) {
120 fprintf(stderr, "Bad local host specification.\n");
121 goto err;
122 }
123 /*
124 * Separate the host and port portions of the
125 * string for further processing.
126 */
127 portstr = last_colon + 1;
128 *last_colon = '\0';
129 port = (unsigned short)atoi(portstr);
130 if ((ret =
131 dbenv->repmgr_site(dbenv, host, port, &dbsite, 0)) != 0){
132 fprintf(stderr, "Could not set local address %s:%d.\n",
133 host, port);
134 goto err;
135 }
136 dbsite->set_config(dbsite, DB_LOCAL_SITE, 1);
137 if (is_group_creator)
138 dbsite->set_config(dbsite, DB_GROUP_CREATOR, 1);
139
140 if ((ret = dbsite->close(dbsite)) != 0) {
141 dbenv->err(dbenv, ret, "DB_SITE->close");
142 goto err;
143 }
144 local_is_set = 1;
145 break;
146 /* Set this replica's election priority. */
147 case 'p':
148 dbenv->rep_set_priority(dbenv, atoi(optarg));
149 break;
150 /* Identify another site in the replication group. */
151 case 'r':
152 host = optarg;
153 /*
154 * The final colon in host:port string is the
155 * boundary between the host and the port portions
156 * of the string.
157 */
158 if ((last_colon = strrchr(host, ':')) == NULL ) {
159 fprintf(stderr, "Bad remote host specification.\n");
160 goto err;
161 }
162 /*
163 * Separate the host and port portions of the
164 * string for further processing.
165 */
166 portstr = last_colon + 1;
167 *last_colon = '\0';
168 port = (unsigned short)atoi(portstr);
169 if ((ret = dbenv->repmgr_site(dbenv, host, port, &dbsite, 0)) != 0) {
170 dbenv->err(dbenv, ret, "DB_ENV->repmgr_site");
171 goto err;
172 }
173 dbsite->set_config(dbsite, DB_BOOTSTRAP_HELPER, 1);
174 if ((ret = dbsite->close(dbsite)) != 0) {
175 dbenv->err(dbenv, ret, "DB_SITE->close");
176 goto err;
177 }
178 break;
179 case '?':
180 default:
181 usage();
182 }
183
184 /* Error check command line. */
185 if (home == NULL || !local_is_set)
186 usage();
187
188 if ((ret = env_init(dbenv, home)) != 0)
189 goto err;
190
191 if ((ret = dbenv->repmgr_start(dbenv, 3, DB_REP_ELECTION)) != 0)
192 goto err;
193
194 if ((ret = doloop(dbenv)) != 0) {
195 dbenv->err(dbenv, ret, "Application failed");
196 goto err;
197 }
198
199 err: if (dbenv != NULL)
200 (void)dbenv->close(dbenv, 0);
201
202 return (ret);
203
204 }
205
206 /* Create and configure an environment handle. */
207 int
create_env(const char * progname,DB_ENV ** dbenvp)208 create_env(const char *progname, DB_ENV **dbenvp)
209 {
210 DB_ENV *dbenv;
211 int ret;
212
213 if ((ret = db_env_create(&dbenv, 0)) != 0) {
214 fprintf(stderr, "can't create env handle: %s\n",
215 db_strerror(ret));
216 return (ret);
217 }
218
219 dbenv->set_errfile(dbenv, stderr);
220 dbenv->set_errpfx(dbenv, progname);
221 (void)dbenv->set_event_notify(dbenv, event_callback);
222
223 *dbenvp = dbenv;
224 return (0);
225 }
226
227 /* Open and configure an environment. */
228 int
env_init(DB_ENV * dbenv,const char * home)229 env_init(DB_ENV *dbenv, const char *home)
230 {
231 u_int32_t flags;
232 int ret;
233
234 (void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
235 (void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
236
237 flags = DB_CREATE |
238 DB_INIT_LOCK |
239 DB_INIT_LOG |
240 DB_INIT_MPOOL |
241 DB_INIT_REP |
242 DB_INIT_TXN |
243 DB_RECOVER |
244 DB_THREAD;
245 if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
246 dbenv->err(dbenv, ret, "can't open environment");
247 return (ret);
248 }
249
250 /*
251 * A callback used to determine whether the local environment is a
252 * replica or a master. This is called by the Replication Manager
253 * when the local environment changes state.
254 */
255 static void
event_callback(DB_ENV * dbenv,u_int32_t which,void * info)256 event_callback(DB_ENV *dbenv, u_int32_t which, void *info)
257 {
258 APP_DATA *app = dbenv->app_private;
259
260 info = NULL; /* Currently unused. */
261
262 switch (which) {
263 case DB_EVENT_REP_MASTER:
264 app->is_master = 1;
265 break;
266
267 case DB_EVENT_REP_CLIENT:
268 app->is_master = 0;
269 break;
270
271 case DB_EVENT_REP_STARTUPDONE: /* FALLTHROUGH */
272 case DB_EVENT_REP_NEWMASTER:
273 /* Ignore. */
274 break;
275
276 default:
277 dbenv->errx(dbenv, "ignoring event %d", which);
278 }
279 }
280
281 /*
282 * Provides the main data processing function for our application.
283 * This function provides a command line prompt to which the user
284 * can provide a ticker string and a stock price. Once a value is
285 * entered to the application, the application writes the value to
286 * the database and then displays the entire database.
287 */
288 #define BUFSIZE 1024
289
290 int
doloop(DB_ENV * dbenv)291 doloop(DB_ENV *dbenv)
292 {
293 DB *dbp;
294 APP_DATA *app_data;
295 DBT key, data;
296 char buf[BUFSIZE], *rbuf;
297 int ret;
298 u_int32_t flags;
299
300 dbp = NULL;
301 ret = 0;
302 memset(&key, 0, sizeof(key));
303 memset(&data, 0, sizeof(data));
304 app_data = dbenv->app_private;
305
306 for (;;) {
307 if (dbp == NULL) {
308 if ((ret = db_create(&dbp, dbenv, 0)) != 0)
309 return (ret);
310
311 flags = DB_AUTO_COMMIT;
312 if (app_data->is_master)
313 flags |= DB_CREATE;
314 if ((ret = dbp->open(dbp,
315 NULL, DATABASE, NULL, DB_BTREE, flags, 0)) != 0) {
316 if (ret == ENOENT) {
317 printf(
318 "No stock database yet available.\n");
319 if ((ret = dbp->close(dbp, 0)) != 0) {
320 dbenv->err(dbenv, ret, "DB->close");
321 goto err;
322 }
323 dbp = NULL;
324 sleep(SLEEPTIME);
325 continue;
326 }
327 dbenv->err(dbenv, ret, "DB->open");
328 goto err;
329 }
330 }
331
332 printf("QUOTESERVER%s> ",
333 app_data->is_master ? "" : " (read-only)");
334 fflush(stdout);
335
336 if (fgets(buf, sizeof(buf), stdin) == NULL)
337 break;
338 if (strtok(&buf[0], " \t\n") == NULL) {
339 switch ((ret = print_stocks(dbp))) {
340 case 0:
341 continue;
342 case DB_REP_HANDLE_DEAD:
343 (void)dbp->close(dbp, DB_NOSYNC);
344 dbp = NULL;
345 dbenv->errx(dbenv, "Got a dead replication handle");
346 continue;
347 default:
348 dbp->err(dbp, ret, "Error traversing data");
349 goto err;
350 }
351 }
352 rbuf = strtok(NULL, " \t\n");
353 if (rbuf == NULL || rbuf[0] == '\0') {
354 if (strncmp(buf, "exit", 4) == 0 ||
355 strncmp(buf, "quit", 4) == 0)
356 break;
357 dbenv->errx(dbenv, "Format: TICKER VALUE");
358 continue;
359 }
360
361 if (!app_data->is_master) {
362 dbenv->errx(dbenv, "Can't update at client");
363 continue;
364 }
365
366 key.data = buf;
367 key.size = (u_int32_t)strlen(buf);
368
369 data.data = rbuf;
370 data.size = (u_int32_t)strlen(rbuf);
371
372 if ((ret = dbp->put(dbp,
373 NULL, &key, &data, 0)) != 0) {
374 dbp->err(dbp, ret, "DB->put");
375 goto err;
376 }
377 }
378
379 err: if (dbp != NULL)
380 (void)dbp->close(dbp, DB_NOSYNC);
381
382 return (ret);
383 }
384
385 /* Display all the stock quote information in the database. */
386 int
print_stocks(DB * dbp)387 print_stocks(DB *dbp)
388 {
389 DBC *dbc;
390 DBT key, data;
391 #define MAXKEYSIZE 10
392 #define MAXDATASIZE 20
393 char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];
394 int ret, t_ret;
395 u_int32_t keysize, datasize;
396
397 if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
398 dbp->err(dbp, ret, "can't open cursor");
399 return (ret);
400 }
401
402 memset(&key, 0, sizeof(key));
403 memset(&data, 0, sizeof(data));
404
405 printf("\tSymbol\tPrice\n");
406 printf("\t======\t=====\n");
407
408 for (ret = dbc->c_get(dbc, &key, &data, DB_FIRST);
409 ret == 0;
410 ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) {
411 keysize = key.size > MAXKEYSIZE ? MAXKEYSIZE : key.size;
412 memcpy(keybuf, key.data, keysize);
413 keybuf[keysize] = '\0';
414
415 datasize = data.size >= MAXDATASIZE ? MAXDATASIZE : data.size;
416 memcpy(databuf, data.data, datasize);
417 databuf[datasize] = '\0';
418
419 printf("\t%s\t%s\n", keybuf, databuf);
420 }
421 printf("\n");
422 fflush(stdout);
423
424 if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
425 ret = t_ret;
426
427 switch (ret) {
428 case 0:
429 case DB_NOTFOUND:
430 return (0);
431 case DB_LOCK_DEADLOCK:
432 return (0);
433 default:
434 return (ret);
435 }
436 }
437
438