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