mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-17 01:26:25 +00:00
1266 lines
35 KiB
C
1266 lines
35 KiB
C
|
/*-
|
||
|
* See the file LICENSE for redistribution information.
|
||
|
*
|
||
|
* Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
|
||
|
*/
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <stdio.h>
|
||
|
#include <db.h>
|
||
|
#include <assert.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
#ifndef _WIN32
|
||
|
#include <pthread.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/select.h>
|
||
|
#include <sys/uio.h>
|
||
|
#endif
|
||
|
|
||
|
#include "../cutest/CuTest.h"
|
||
|
#include "../common/test_util.h"
|
||
|
|
||
|
#define MAX_SEGS 10
|
||
|
#define MAX_MSGS 10
|
||
|
|
||
|
#undef LOCK_MUTEX
|
||
|
#undef UNLOCK_MUTEX
|
||
|
#ifdef _WIN32
|
||
|
#define sleep(s) Sleep(1000 * (s))
|
||
|
typedef HANDLE mutex_t;
|
||
|
#define mutex_init(m) \
|
||
|
(((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
|
||
|
#define mutex_lock(m) \
|
||
|
((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? \
|
||
|
0 : GetLastError())
|
||
|
#define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : GetLastError())
|
||
|
#define mutex_destroy(m) (CloseHandle(*(m)) ? 0 : GetLastError())
|
||
|
typedef HANDLE cond_t;
|
||
|
#define cond_init(c) ((*(c) = CreateEvent(NULL, \
|
||
|
TRUE, FALSE, NULL)) == NULL ? GetLastError() : 0)
|
||
|
#define cond_wait(c, m) (SignalObjectAndWait(*(m), *(c), INFINITE, FALSE) == WAIT_OBJECT_0 ? \
|
||
|
0 : GetLastError())
|
||
|
#define cond_wake(c) (SetEvent(*(c)) ? 0 : GetLastError())
|
||
|
#else
|
||
|
typedef pthread_mutex_t mutex_t;
|
||
|
#define mutex_init(m) pthread_mutex_init((m), NULL)
|
||
|
#define mutex_lock(m) pthread_mutex_lock(m)
|
||
|
#define mutex_unlock(m) pthread_mutex_unlock(m)
|
||
|
#define mutex_destroy(m) pthread_mutex_destroy(m)
|
||
|
typedef pthread_cond_t cond_t;
|
||
|
#define cond_init(c) pthread_cond_init((c), NULL)
|
||
|
#define cond_wait(c, m) pthread_cond_wait((c), (m))
|
||
|
#define cond_wake(c) pthread_cond_broadcast(c)
|
||
|
#endif
|
||
|
|
||
|
struct channel_test_globals {
|
||
|
CuTest *test;
|
||
|
mutex_t mtx;
|
||
|
cond_t cond;
|
||
|
};
|
||
|
|
||
|
struct report {
|
||
|
int dbt_count;
|
||
|
DBT dbt[MAX_SEGS];
|
||
|
|
||
|
int msg_count;
|
||
|
char *msg[MAX_MSGS];
|
||
|
|
||
|
int done, ret;
|
||
|
};
|
||
|
|
||
|
struct reports {
|
||
|
mutex_t m;
|
||
|
int count;
|
||
|
struct report rpt[2];
|
||
|
};
|
||
|
|
||
|
struct env_info {
|
||
|
struct report *rpt;
|
||
|
struct reports *rpts;
|
||
|
struct channel_test_globals *g;
|
||
|
int startupdone;
|
||
|
};
|
||
|
|
||
|
struct msginfo {
|
||
|
DB_ENV *dbenv;
|
||
|
int count;
|
||
|
};
|
||
|
|
||
|
typedef int (*PRED) __P((void *));
|
||
|
|
||
|
static int await_condition __P((PRED, void *, long));
|
||
|
static int check_dbt_string __P((DBT *, const char *));
|
||
|
static void clear_rpt __P((DB_ENV *));
|
||
|
static void clear_rpt_int __P((struct report *));
|
||
|
static int env_done __P((void *));
|
||
|
static struct report *get_rpt __P((const DB_ENV *));
|
||
|
static int fortify __P((DB_ENV *, struct channel_test_globals *));
|
||
|
static int has_msgs __P((void *));
|
||
|
static void msg_disp __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t));
|
||
|
static void msg_disp2 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t));
|
||
|
static void msg_disp3 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t));
|
||
|
static void msg_disp4 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t));
|
||
|
static void msg_disp5 __P((DB_ENV *, DB_CHANNEL *, DBT *, u_int32_t, u_int32_t));
|
||
|
static int mystrcmp __P((char *, const char *));
|
||
|
static void notify __P((DB_ENV *, u_int32_t, void *));
|
||
|
static int is_started __P((void *));
|
||
|
static void td __P((DB_ENV *));
|
||
|
static void test_data_init __P((DBT *, char *));
|
||
|
static void test_zeroes __P((DB_CHANNEL *, DB_ENV *, CuTest *));
|
||
|
static int two_done __P((void *));
|
||
|
|
||
|
#define LOCK_MUTEX(m) do { \
|
||
|
int __ret; \
|
||
|
__ret = mutex_lock(m); \
|
||
|
assert(__ret == 0); \
|
||
|
} while (0)
|
||
|
|
||
|
#define UNLOCK_MUTEX(m) do { \
|
||
|
int __ret; \
|
||
|
__ret = mutex_unlock(m); \
|
||
|
assert(__ret == 0); \
|
||
|
} while (0)
|
||
|
|
||
|
int TestChannelSuiteSetup(CuSuite *suite) {
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int TestChannelSuiteTeardown(CuSuite *suite) {
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int TestChannelTestSetup(CuTest *test) {
|
||
|
struct channel_test_globals *g;
|
||
|
int ret;
|
||
|
|
||
|
if ((g = calloc(1, sizeof(*g))) == NULL)
|
||
|
return (ENOMEM);
|
||
|
if ((ret = mutex_init(&g->mtx)) != 0) {
|
||
|
free(g);
|
||
|
return (ret);
|
||
|
}
|
||
|
if ((ret = cond_init(&g->cond)) != 0) {
|
||
|
mutex_destroy(&g->mtx);
|
||
|
free(g);
|
||
|
return (ret);
|
||
|
}
|
||
|
g->test = test;
|
||
|
test->context = g;
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int TestChannelTestTeardown(CuTest *test) {
|
||
|
struct channel_test_globals *g;
|
||
|
int ret;
|
||
|
|
||
|
g = test->context;
|
||
|
assert(g != NULL);
|
||
|
ret = mutex_destroy(&g->mtx);
|
||
|
free(g);
|
||
|
test->context = NULL;
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
myerrcall(const DB_ENV *dbenv, const char *errpfx, const char *msg) {
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
|
||
|
assert(rpt->msg_count < MAX_MSGS);
|
||
|
assert((rpt->msg[rpt->msg_count++] = strdup(msg)) != NULL);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
fortify(dbenv, g)
|
||
|
DB_ENV *dbenv;
|
||
|
struct channel_test_globals *g;
|
||
|
{
|
||
|
struct report *rpt;
|
||
|
struct env_info *info;
|
||
|
|
||
|
if ((info = calloc(1, sizeof(*info))) == NULL)
|
||
|
return (ENOMEM);
|
||
|
if ((rpt = calloc(1, sizeof(*rpt))) == NULL) {
|
||
|
free(info);
|
||
|
return (ENOMEM);
|
||
|
}
|
||
|
info->rpt = rpt;
|
||
|
info->rpts = NULL;
|
||
|
info->g = g;
|
||
|
info->startupdone = 0;
|
||
|
dbenv->app_private = info;
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
setup(envp1, envp2, envp3, g)
|
||
|
DB_ENV **envp1, **envp2, **envp3;
|
||
|
struct channel_test_globals *g;
|
||
|
{
|
||
|
DB_ENV *dbenv1, *dbenv2, *dbenv3;
|
||
|
DB_SITE *dbsite;
|
||
|
u_int32_t flags;
|
||
|
int ret;
|
||
|
|
||
|
#define CHECK(call) \
|
||
|
do { \
|
||
|
if ((ret = (call)) != 0) { \
|
||
|
fprintf(stderr, "error %d from %s", ret, #call); \
|
||
|
goto err; \
|
||
|
} \
|
||
|
} while (0);
|
||
|
|
||
|
dbenv1 = dbenv2 = dbenv3 = NULL;
|
||
|
CHECK(db_env_create(&dbenv1, 0));
|
||
|
CHECK(fortify(dbenv1, g));
|
||
|
dbenv1->set_errpfx(dbenv1, "ENV1");
|
||
|
dbenv1->set_errcall(dbenv1, myerrcall);
|
||
|
flags = DB_INIT_REP | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_MPOOL |
|
||
|
DB_INIT_TXN | DB_RECOVER | DB_THREAD | DB_CREATE;
|
||
|
setup_envdir("DIR1", 1);
|
||
|
CHECK(dbenv1->open(dbenv1, "DIR1", flags, 0));
|
||
|
|
||
|
CHECK(dbenv1->rep_set_config(dbenv1, DB_REPMGR_CONF_ELECTIONS, 0));
|
||
|
CHECK(dbenv1->repmgr_site(dbenv1, "localhost", 6000, &dbsite, 0));
|
||
|
CHECK(dbsite->set_config(dbsite, DB_LOCAL_SITE, 1));
|
||
|
CHECK(dbsite->close(dbsite));
|
||
|
CHECK(dbenv1->set_event_notify(dbenv1, notify));
|
||
|
CHECK(dbenv1->repmgr_msg_dispatch(dbenv1, msg_disp, 0));
|
||
|
CHECK(dbenv1->rep_set_timeout(dbenv1,
|
||
|
DB_REP_CONNECTION_RETRY, 3000000));
|
||
|
CHECK(dbenv1->repmgr_start(dbenv1, 2, DB_REP_MASTER));
|
||
|
|
||
|
CHECK(db_env_create(&dbenv2, 0));
|
||
|
CHECK(fortify(dbenv2, g));
|
||
|
dbenv2->set_errpfx(dbenv2, "ENV2");
|
||
|
dbenv2->set_errcall(dbenv2, myerrcall);
|
||
|
setup_envdir("DIR2", 1);
|
||
|
CHECK(dbenv2->open(dbenv2, "DIR2", flags, 0));
|
||
|
CHECK(dbenv2->rep_set_config(dbenv2, DB_REPMGR_CONF_ELECTIONS, 0));
|
||
|
|
||
|
CHECK(dbenv2->repmgr_site(dbenv2, "localhost", 6001, &dbsite, 0));
|
||
|
CHECK(dbsite->set_config(dbsite, DB_LOCAL_SITE, 1));
|
||
|
CHECK(dbsite->close(dbsite));
|
||
|
CHECK(dbenv2->repmgr_site(dbenv2, "localhost", 6000, &dbsite, 0));
|
||
|
CHECK(dbsite->set_config(dbsite, DB_BOOTSTRAP_HELPER, 1));
|
||
|
CHECK(dbsite->close(dbsite));
|
||
|
CHECK(dbenv2->set_event_notify(dbenv2, notify));
|
||
|
CHECK(dbenv2->rep_set_timeout(dbenv2,
|
||
|
DB_REP_CONNECTION_RETRY, 3000000));
|
||
|
CHECK(dbenv2->repmgr_start(dbenv2, 2, DB_REP_CLIENT));
|
||
|
|
||
|
await_condition(is_started, dbenv2, 60);
|
||
|
if (!is_started(dbenv2)) {
|
||
|
dbenv2->errx(dbenv2, "startup done not achieved in 60 seconds");
|
||
|
ret = DB_TIMEOUT;
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
CHECK(db_env_create(&dbenv3, 0));
|
||
|
CHECK(fortify(dbenv3, g));
|
||
|
dbenv3->set_errpfx(dbenv3, "ENV3");
|
||
|
dbenv3->set_errcall(dbenv3, myerrcall);
|
||
|
CHECK(dbenv3->repmgr_msg_dispatch(dbenv3, msg_disp2, 0));
|
||
|
setup_envdir("DIR3", 1);
|
||
|
CHECK(dbenv3->open(dbenv3, "DIR3", flags, 0));
|
||
|
CHECK(dbenv3->rep_set_config(dbenv3, DB_REPMGR_CONF_ELECTIONS, 0));
|
||
|
|
||
|
CHECK(dbenv3->repmgr_site(dbenv3, "localhost", 6002, &dbsite, 0));
|
||
|
CHECK(dbsite->set_config(dbsite, DB_LOCAL_SITE, 1));
|
||
|
CHECK(dbsite->close(dbsite));
|
||
|
CHECK(dbenv3->repmgr_site(dbenv3, "localhost", 6000, &dbsite, 0));
|
||
|
CHECK(dbsite->set_config(dbsite, DB_BOOTSTRAP_HELPER, 1));
|
||
|
CHECK(dbsite->close(dbsite));
|
||
|
CHECK(dbenv3->set_event_notify(dbenv3, notify));
|
||
|
CHECK(dbenv3->rep_set_timeout(dbenv3,
|
||
|
DB_REP_CONNECTION_RETRY, 3000000));
|
||
|
CHECK(dbenv3->repmgr_start(dbenv3, 2, DB_REP_CLIENT));
|
||
|
|
||
|
await_condition(is_started, dbenv3, 60);
|
||
|
if (!is_started(dbenv3)) {
|
||
|
dbenv3->errx(dbenv3, "startup done not achieved in 60 seconds");
|
||
|
ret = DB_TIMEOUT;
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
*envp1 = dbenv1;
|
||
|
*envp2 = dbenv2;
|
||
|
*envp3 = dbenv3;
|
||
|
return (0);
|
||
|
|
||
|
err:
|
||
|
if (dbenv3 != NULL)
|
||
|
td(dbenv3);
|
||
|
if (dbenv2 != NULL)
|
||
|
td(dbenv2);
|
||
|
if (dbenv1 != NULL)
|
||
|
td(dbenv1);
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
td(dbenv)
|
||
|
DB_ENV *dbenv;
|
||
|
{
|
||
|
struct env_info *info;
|
||
|
|
||
|
dbenv->set_errcall(dbenv, NULL);
|
||
|
dbenv->set_event_notify(dbenv, NULL);
|
||
|
|
||
|
info = dbenv->app_private;
|
||
|
dbenv->close(dbenv, 0);
|
||
|
if (info != NULL) {
|
||
|
clear_rpt_int(info->rpt);
|
||
|
free(info->rpt);
|
||
|
free(info);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
clear_rpt_int(rpt)
|
||
|
struct report *rpt;
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < rpt->dbt_count; i++)
|
||
|
free(rpt->dbt[i].data);
|
||
|
rpt->dbt_count = 0;
|
||
|
|
||
|
for (i = 0; i < rpt->msg_count; i++)
|
||
|
free(rpt->msg[i]);
|
||
|
rpt->msg_count = 0;
|
||
|
|
||
|
rpt->done = 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
clear_rpt(dbenv)
|
||
|
DB_ENV *dbenv;
|
||
|
{
|
||
|
struct env_info *info;
|
||
|
struct report *rpt;
|
||
|
|
||
|
info = dbenv->app_private;
|
||
|
rpt = info->rpt;
|
||
|
clear_rpt_int(rpt);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
env_done(ctx)
|
||
|
void *ctx;
|
||
|
{
|
||
|
DB_ENV *dbenv = ctx;
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
|
||
|
return (rpt->done);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
await_done(dbenv)
|
||
|
DB_ENV *dbenv;
|
||
|
{
|
||
|
await_condition(env_done, dbenv, 60);
|
||
|
assert(env_done(dbenv));
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
has_msgs(ctx)
|
||
|
void *ctx;
|
||
|
{
|
||
|
struct msginfo *inf = ctx;
|
||
|
DB_ENV *dbenv = inf->dbenv;
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
|
||
|
return (rpt->msg_count == inf->count);
|
||
|
}
|
||
|
|
||
|
static struct report *
|
||
|
get_rpt(dbenv)
|
||
|
const DB_ENV *dbenv;
|
||
|
{
|
||
|
struct env_info *info;
|
||
|
|
||
|
if ((info = dbenv->app_private) == NULL)
|
||
|
return (NULL);
|
||
|
return (info->rpt);
|
||
|
}
|
||
|
|
||
|
int TestChannelFeature(CuTest *ct) {
|
||
|
DB_ENV *dbenv1, *dbenv2, *dbenv3;
|
||
|
DB_CHANNEL *ch;
|
||
|
DB_REP_STAT *stats;
|
||
|
DB_SITE *dbsite;
|
||
|
DBT dbt, rdbts[10], resp;
|
||
|
struct channel_test_globals *g;
|
||
|
struct report *rpt;
|
||
|
struct reports rpts;
|
||
|
struct msginfo info;
|
||
|
char *p;
|
||
|
void *pointer, *vp, *buffer;
|
||
|
u_int8_t short_buf[4];
|
||
|
size_t sz;
|
||
|
int done, eid, ret;
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||
|
#endif
|
||
|
printf("this is a test for repmgr channels feature\n");
|
||
|
|
||
|
g = ct->context;
|
||
|
CuAssertTrue(ct, (ret = setup(&dbenv1, &dbenv2, &dbenv3, g) == 0));
|
||
|
|
||
|
/*
|
||
|
* For this first section, we're sending to ENV2. TODO: make that more
|
||
|
* clear, without duplicating the hard-coding of the port.
|
||
|
*/
|
||
|
CuAssertTrue(ct,
|
||
|
(ret = dbenv1->repmgr_site(dbenv1,
|
||
|
"localhost", 6001, &dbsite, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->get_eid(dbsite, &eid)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->close(dbsite)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv1->repmgr_channel(dbenv1, eid, &ch, 0)) == 0);
|
||
|
|
||
|
memset(&dbt, 0, sizeof(dbt));
|
||
|
p = "foobar";
|
||
|
dbt.data = p;
|
||
|
dbt.size = (u_int32_t)strlen(p) + 1;
|
||
|
memset(&resp, 0, sizeof(resp));
|
||
|
resp.flags = DB_DBT_MALLOC;
|
||
|
printf("1. send async msg with no msg dispatch in place\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, (ret = ch->send_msg(ch, &dbt, 1, 0)) == 0);
|
||
|
|
||
|
/* Wait til dbenv2 has reported 1 msg. */
|
||
|
info.dbenv = dbenv2;
|
||
|
info.count = 1;
|
||
|
await_condition(has_msgs, &info, 60);
|
||
|
rpt = get_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 1);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"No message dispatch call-back function has been configured") == 0);
|
||
|
|
||
|
printf("2. send request with no msg dispatch in place\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
ret = ch->send_request(ch, &dbt, 1, &resp, 0, 0);
|
||
|
CuAssertTrue(ct, ret == DB_NOSERVER);
|
||
|
if (resp.data != NULL)
|
||
|
free(resp.data);
|
||
|
await_condition(has_msgs, &info, 60);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 1);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"No message dispatch call-back function has been configured") == 0);
|
||
|
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_msg_dispatch(dbenv2, msg_disp, 0)) == 0);
|
||
|
|
||
|
printf("3. send request where recip forgot resp\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
ret = ch->send_request(ch, &dbt, 1, &resp, 0, 0);
|
||
|
CuAssertTrue(ct, ret == DB_KEYEMPTY);
|
||
|
if (resp.data != NULL)
|
||
|
free(resp.data);
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 1);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"Application failed to provide a response") == 0);
|
||
|
|
||
|
printf("4. now with dispatch fn installed, send a simple async msg\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
test_data_init(&dbt, "Mr. Watson -- come here -- I want to see you.");
|
||
|
CuAssertTrue(ct, (ret = ch->send_msg(ch, &dbt, 1, 0)) == 0);
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0],
|
||
|
"Mr. Watson -- come here -- I want to see you.");
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
|
||
|
printf("5. send a multi-seg request\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
memset(&resp, 0, sizeof(resp));
|
||
|
resp.flags = DB_DBT_MALLOC;
|
||
|
test_data_init(&rdbts[0], "I wish I were a fish");
|
||
|
test_data_init(&rdbts[1], "I wish I were a bass");
|
||
|
test_data_init(&rdbts[2],
|
||
|
"I'd climb up on a slippery rock and slide down on my ... hands and knees");
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 3, &resp, 0, 0)) == 0);
|
||
|
check_dbt_string(&resp, "this is the answer to the request");
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 3);
|
||
|
check_dbt_string(&rpt->dbt[0], "I wish I were a fish");
|
||
|
check_dbt_string(&rpt->dbt[1], "I wish I were a bass");
|
||
|
check_dbt_string(&rpt->dbt[2],
|
||
|
"I'd climb up on a slippery rock and slide down on my ... hands and knees");
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
|
||
|
test_zeroes(ch, dbenv2, ct);
|
||
|
|
||
|
printf("7. send request with too-small USERMEM buffer\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
resp.data = short_buf;
|
||
|
resp.ulen = sizeof(short_buf);
|
||
|
resp.flags = DB_DBT_USERMEM;
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 3, &resp, 0, 0)) == DB_BUFFER_SMALL);
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 1);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"originator's USERMEM buffer too small") == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == EINVAL);
|
||
|
|
||
|
#define BUFLEN 20000
|
||
|
buffer = malloc(BUFLEN);
|
||
|
if (buffer == NULL)
|
||
|
return (2);
|
||
|
resp.data = buffer;
|
||
|
resp.ulen = BUFLEN;
|
||
|
resp.flags = DB_DBT_USERMEM;
|
||
|
|
||
|
printf("8. send USERMEM request without necessary DB_MULTIPLE\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 2, &resp, 0, 0)) == DB_BUFFER_SMALL);
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 1);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"originator does not accept multi-segment response") == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == EINVAL);
|
||
|
|
||
|
printf("9. send USERMEM request with DB_MULTIPLE\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 2, &resp, 0, DB_MULTIPLE)) == 0);
|
||
|
DB_MULTIPLE_INIT(pointer, &resp);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "roses are red") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "violets are blue") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, pointer == NULL);
|
||
|
|
||
|
ch->close(ch, 0);
|
||
|
|
||
|
|
||
|
/* ------------------------------- */
|
||
|
|
||
|
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, DB_EID_MASTER, &ch, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv1->repmgr_msg_dispatch(dbenv1, msg_disp2, 0)) == 0);
|
||
|
|
||
|
// do a request to master
|
||
|
// switch masters
|
||
|
// do a request to new master
|
||
|
printf("(now we try a couple of operations on a master channel)\n");
|
||
|
|
||
|
printf("10. send request to original master\n");
|
||
|
rpt = get_rpt(dbenv1);
|
||
|
clear_rpt(dbenv1);
|
||
|
resp.data = buffer;
|
||
|
resp.ulen = BUFLEN;
|
||
|
resp.flags = DB_DBT_USERMEM;
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 1, &resp, 0, 0)) == 0);
|
||
|
check_dbt_string(&resp, "ENV1");
|
||
|
await_done(dbenv1);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0], "I wish I were a fish");
|
||
|
|
||
|
printf("switch master and wait for our client to see the change\n");
|
||
|
((struct env_info *)dbenv2->app_private)->startupdone = 0;
|
||
|
CuAssertTrue(ct, (ret = dbenv1->repmgr_start(dbenv1, 0, DB_REP_CLIENT)) == 0);
|
||
|
sleep(1); /* workaround for 19329 */
|
||
|
for (done = 0; ; ) {
|
||
|
/*
|
||
|
* Become master, and then make sure it really happened.
|
||
|
* Occasionally a race develops, where we're still holding on to
|
||
|
* the msg lockout at env3 at this point, in which case the
|
||
|
* rep_start() call (underlying our repmgr_start() call here) is
|
||
|
* simply dropped on the floor.
|
||
|
*/
|
||
|
CuAssertTrue(ct, (ret = dbenv3->repmgr_start(dbenv3,
|
||
|
0, DB_REP_MASTER)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv3->rep_stat(dbenv3,
|
||
|
&stats, 0)) == 0);
|
||
|
done = stats->st_status == DB_REP_MASTER;
|
||
|
free(stats);
|
||
|
if (done)
|
||
|
break;
|
||
|
sleep(1);
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* !!!
|
||
|
* Workaround for 19297: wait until verify dance is complete at env2,
|
||
|
* because (just a little bit) later we're going to switch master again,
|
||
|
* to env2. If rep_start(MASTER) at env2 happens while processing
|
||
|
* VERIFY match record, core rep ignores the rep_start() (even though it
|
||
|
* returns 0).
|
||
|
*/
|
||
|
LOCK_MUTEX(&g->mtx);
|
||
|
while (!((struct env_info *)dbenv2->app_private)->startupdone) {
|
||
|
cond_wait(&g->cond, &g->mtx);
|
||
|
}
|
||
|
// TODO: fix these macros so that this ridiculous hack isn't necessary
|
||
|
#ifndef _WIN32
|
||
|
UNLOCK_MUTEX(&g->mtx);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
printf("11. send request which should go to new master (only)\n");
|
||
|
clear_rpt(dbenv1);
|
||
|
clear_rpt(dbenv3);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 1, &resp, 0, 0)) == 0);
|
||
|
check_dbt_string(&resp, "ENV3");
|
||
|
rpt = get_rpt(dbenv3);
|
||
|
await_done(dbenv3);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0], "I wish I were a fish");
|
||
|
rpt = get_rpt(dbenv1);
|
||
|
CuAssertTrue(ct, !rpt->done); /* old master shouldn't have recvd anything */
|
||
|
|
||
|
printf("switch master again, to ``self''\n");
|
||
|
CuAssertTrue(ct, (ret = dbenv3->repmgr_start(dbenv3, 0, DB_REP_CLIENT)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_start(dbenv2, 0, DB_REP_MASTER)) == 0);
|
||
|
/* No need to wait for env2 to see that env2 has become master. */
|
||
|
|
||
|
clear_rpt(dbenv1);
|
||
|
clear_rpt(dbenv2);
|
||
|
clear_rpt(dbenv3);
|
||
|
printf("12. send to self, async\n");
|
||
|
CuAssertTrue(ct, (ret = ch->send_msg(ch, &dbt, 1, 0)) == 0);
|
||
|
await_done(dbenv2);
|
||
|
rpt = get_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0],
|
||
|
"Mr. Watson -- come here -- I want to see you.");
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
printf(" (check that other two sites didn't receive it)\n");
|
||
|
sleep(1);
|
||
|
CuAssertTrue(ct, !get_rpt(dbenv1)->done);
|
||
|
CuAssertTrue(ct, !get_rpt(dbenv3)->done);
|
||
|
|
||
|
printf("13. send-to-self request\n");
|
||
|
clear_rpt(dbenv2);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 2, &resp, 0, DB_MULTIPLE)) == 0);
|
||
|
DB_MULTIPLE_INIT(pointer, &resp);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "roses are red") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "violets are blue") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, pointer == NULL);
|
||
|
|
||
|
/*
|
||
|
* re-test the 0-length cases, in the send-to-self context (the
|
||
|
* implementation has a bunch of separate code)
|
||
|
*/
|
||
|
test_zeroes(ch, dbenv2, ct);
|
||
|
|
||
|
ch->close(ch, 0);
|
||
|
|
||
|
/* ---------------------------------------- */
|
||
|
|
||
|
// If you go from env2 to env, we know that it's port 6000. TODO: make this
|
||
|
// more robust by storing it some more structured form.
|
||
|
//
|
||
|
CuAssertTrue(ct, (ret = dbenv1->repmgr_msg_dispatch(dbenv1, msg_disp3, 0)) == 0);
|
||
|
CuAssertTrue(ct,
|
||
|
(ret = dbenv2->repmgr_site(dbenv2,
|
||
|
"localhost", 6000, &dbsite, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->get_eid(dbsite, &eid)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->close(dbsite)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, eid, &ch, 0)) == 0);
|
||
|
|
||
|
printf("14. send request to site that has been shut down\n");
|
||
|
td(dbenv1);
|
||
|
memset(&resp, 0, sizeof(resp));
|
||
|
resp.flags = DB_DBT_MALLOC;
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 2, &resp, 0, 0)) ==
|
||
|
DB_REP_UNAVAIL);
|
||
|
if (resp.data != NULL)
|
||
|
free(resp.data);
|
||
|
|
||
|
// TODO: a much more interesting case is to have the remote site shut
|
||
|
// down while waiting for the response, because that exercises some
|
||
|
// clean-up code. But I guess that requires running in a couple of
|
||
|
// threads.
|
||
|
|
||
|
ch->close(ch, 0);
|
||
|
|
||
|
printf("15. try to connect to a down site\n");
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, eid, &ch, 0)) == DB_REP_UNAVAIL);
|
||
|
|
||
|
printf("16. try to connect to a non-existent EID\n");
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, 1732, &ch, 0)) == EINVAL);
|
||
|
|
||
|
printf("17. connect master to self from the start\n");
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, DB_EID_MASTER, &ch, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_msg_dispatch(dbenv2, msg_disp2, 0)) == 0);
|
||
|
rpt = get_rpt(dbenv2);
|
||
|
clear_rpt(dbenv2);
|
||
|
resp.data = buffer;
|
||
|
resp.ulen = BUFLEN;
|
||
|
resp.flags = DB_DBT_USERMEM;
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 1, &resp, 0, 0)) == 0);
|
||
|
check_dbt_string(&resp, "ENV2");
|
||
|
await_done(dbenv2);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0], "I wish I were a fish");
|
||
|
|
||
|
ch->close(ch, 0);
|
||
|
|
||
|
/*
|
||
|
* Send an async message from env2 to env3, at which point env3 will
|
||
|
* reply by returning two async messages back to env2.
|
||
|
*/
|
||
|
printf("18. test async replies to (async) messages\n");
|
||
|
CuAssertTrue(ct, (ret = dbenv3->repmgr_msg_dispatch(dbenv3, msg_disp3, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_msg_dispatch(dbenv2, msg_disp4, 0)) == 0);
|
||
|
CuAssertTrue(ct,
|
||
|
(ret = dbenv2->repmgr_site(dbenv2,
|
||
|
"localhost", 6002, &dbsite, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->get_eid(dbsite, &eid)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbsite->close(dbsite)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, eid, &ch, 0)) == 0);
|
||
|
rpt = get_rpt(dbenv3);
|
||
|
clear_rpt(dbenv3);
|
||
|
((struct env_info *)dbenv2->app_private)->rpts = &rpts;
|
||
|
memset(&rpts, 0, sizeof(rpts));
|
||
|
mutex_init(&rpts.m);
|
||
|
CuAssertTrue(ct, (ret = ch->send_msg(ch, rdbts, 1, 0)) == 0);
|
||
|
await_done(dbenv3);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 1);
|
||
|
check_dbt_string(&rpt->dbt[0], "I wish I were a fish");
|
||
|
CuAssertTrue(ct, await_condition(two_done, dbenv2, 10));
|
||
|
CuAssertTrue(ct, rpts.rpt[0].done);
|
||
|
CuAssertTrue(ct, rpts.rpt[0].dbt_count == 1);
|
||
|
check_dbt_string(&rpts.rpt[0].dbt[0], "roses may be pink");
|
||
|
|
||
|
CuAssertTrue(ct, rpts.rpt[1].done);
|
||
|
CuAssertTrue(ct, rpts.rpt[1].dbt_count == 1);
|
||
|
check_dbt_string(&rpts.rpt[1].dbt[0], "I think");
|
||
|
clear_rpt_int(&rpts.rpt[0]);
|
||
|
clear_rpt_int(&rpts.rpt[1]);
|
||
|
|
||
|
ch->close(ch, 0);
|
||
|
sleep(1); /* wait for "EOF on connection" msg before cleaning, below */
|
||
|
// This kluge disappears when GM fixes that err msg to become an event
|
||
|
|
||
|
printf("19. test illegal calls from the msg disp function\n");
|
||
|
clear_rpt(dbenv3);
|
||
|
CuAssertTrue(ct, (ret = dbenv3->repmgr_msg_dispatch(dbenv3, msg_disp5, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = dbenv2->repmgr_channel(dbenv2, eid, &ch, 0)) == 0);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 1, &resp, 0, 0)) == 0);
|
||
|
await_done(dbenv3);
|
||
|
rpt = get_rpt(dbenv3);
|
||
|
CuAssertTrue(ct, rpt->ret == EINVAL);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 3);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[0],
|
||
|
"set_timeout() invalid on DB_CHANNEL supplied to msg dispatch function") == 0);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[1],
|
||
|
"close() invalid on DB_CHANNEL supplied to msg dispatch function") == 0);
|
||
|
CuAssertTrue(ct, mystrcmp(rpt->msg[2],
|
||
|
"send_request() invalid on DB_CHANNEL supplied to msg dispatch function") == 0);
|
||
|
ch->close(ch, 0);
|
||
|
|
||
|
free(buffer);
|
||
|
|
||
|
td(dbenv2);
|
||
|
td(dbenv3);
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
two_done(ctx)
|
||
|
void *ctx;
|
||
|
{
|
||
|
DB_ENV *dbenv = ctx;
|
||
|
struct reports *rpts = ((struct env_info *)dbenv->app_private)->rpts;
|
||
|
|
||
|
return (rpts->count == 2 && rpts->rpt[0].done && rpts->rpt[1].done);
|
||
|
}
|
||
|
|
||
|
/* return 1 ("true") for a match, 0 ("false") otherwise */
|
||
|
static int
|
||
|
check_dbt_string(dbt, s)
|
||
|
DBT *dbt;
|
||
|
const char *s;
|
||
|
{
|
||
|
if (dbt->size != strlen(s))
|
||
|
return (0);
|
||
|
if (dbt->size == 0)
|
||
|
return (1);
|
||
|
return (strcmp((char*)dbt->data, s) == 0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
is_started(ctx)
|
||
|
void *ctx;
|
||
|
{
|
||
|
DB_ENV *dbenv = ctx;
|
||
|
DB_REP_STAT *st;
|
||
|
u_int32_t ans;
|
||
|
int ret;
|
||
|
|
||
|
if ((ret = dbenv->rep_stat(dbenv, &st, 0)) != 0) {
|
||
|
dbenv->err(dbenv, ret, "rep_stat");
|
||
|
return (0);
|
||
|
}
|
||
|
ans = st->st_startup_complete;
|
||
|
free(st);
|
||
|
return (ans);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
await_condition(pred, ctx, limit)
|
||
|
PRED pred;
|
||
|
void *ctx;
|
||
|
long limit;
|
||
|
{
|
||
|
#ifndef _WIN32
|
||
|
struct timeval t;
|
||
|
#endif
|
||
|
time_t tim;
|
||
|
|
||
|
tim = time(NULL) + limit;
|
||
|
while (time(NULL) < tim) {
|
||
|
if ((*pred)(ctx))
|
||
|
return (1);
|
||
|
// sleep 1/10th of a second at a time
|
||
|
// (maybe Windows can use select() too, if include Winsock2.h)
|
||
|
#ifdef _WIN32
|
||
|
Sleep(100);
|
||
|
#else
|
||
|
t.tv_sec = 0;
|
||
|
t.tv_usec = 100000;
|
||
|
select(0, NULL, NULL, NULL, &t);
|
||
|
#endif
|
||
|
}
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
notify(dbenv, event, unused)
|
||
|
DB_ENV *dbenv;
|
||
|
u_int32_t event;
|
||
|
void *unused;
|
||
|
{
|
||
|
struct channel_test_globals *g;
|
||
|
struct env_info *info;
|
||
|
|
||
|
if (event == DB_EVENT_PANIC) {
|
||
|
fprintf(stderr, "BDB panic");
|
||
|
abort();
|
||
|
} else if (event == DB_EVENT_REP_STARTUPDONE) {
|
||
|
info = dbenv->app_private;
|
||
|
g = info->g;
|
||
|
LOCK_MUTEX(&g->mtx);
|
||
|
info->startupdone = 1;
|
||
|
cond_wake(&g->cond);
|
||
|
UNLOCK_MUTEX(&g->mtx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
msg_disp(dbenv, ch, request, nseg, flags)
|
||
|
DB_ENV *dbenv;
|
||
|
DB_CHANNEL *ch;
|
||
|
DBT *request;
|
||
|
u_int32_t nseg;
|
||
|
u_int32_t flags;
|
||
|
{
|
||
|
CuTest *ct;
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
DBT answer, mult[3];
|
||
|
char *p;
|
||
|
size_t sz;
|
||
|
u_int32_t i;
|
||
|
int ret;
|
||
|
|
||
|
ct = ((struct env_info *)dbenv->app_private)->g->test;
|
||
|
CuAssertTrue(ct, nseg < MAX_SEGS);
|
||
|
for (i = 0; i < nseg; i++) {
|
||
|
if ((sz = (rpt->dbt[rpt->dbt_count].size = request[i].size)) > 0) {
|
||
|
CuAssertTrue(ct, (rpt->dbt[rpt->dbt_count].data = malloc(sz)) != NULL);
|
||
|
memcpy(rpt->dbt[rpt->dbt_count].data,
|
||
|
request[i].data, sz);
|
||
|
} else
|
||
|
rpt->dbt[rpt->dbt_count].data = NULL;
|
||
|
rpt->dbt_count++;
|
||
|
}
|
||
|
|
||
|
ret = 0;
|
||
|
if (flags & DB_REPMGR_NEED_RESPONSE) {
|
||
|
if (nseg == 2) {
|
||
|
/* Try a multi-segment response. */
|
||
|
memset(&mult, 0, sizeof(mult));
|
||
|
p = "roses are red";
|
||
|
mult[0].data = p;
|
||
|
mult[0].size = (u_int32_t)strlen(p) + 1;
|
||
|
p = "violets are blue";
|
||
|
mult[1].data = p;
|
||
|
mult[1].size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &mult[0], 2, 0);
|
||
|
} else if (nseg == 1) {
|
||
|
// pretend to ``forget'' to respond
|
||
|
} else if (nseg == 4) {
|
||
|
// send a response of zero segments
|
||
|
ret = ch->send_msg(ch, &answer, 0, 0);
|
||
|
} else if (nseg == 5) {
|
||
|
// send a response with a segment of zero length
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
answer.size = 0;
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
|
||
|
// TODO: we still need to try this with the DB_MULTIPLE approach too
|
||
|
} else if (nseg == 6) {
|
||
|
// patience, ...
|
||
|
/* Try a multi-segment response. */
|
||
|
memset(&mult, 0, sizeof(mult));
|
||
|
p = "roses are red";
|
||
|
mult[0].data = p;
|
||
|
mult[0].size = (u_int32_t)strlen(p) + 1;
|
||
|
p = "violets are blue";
|
||
|
mult[1].size = 0;
|
||
|
mult[2].data = p;
|
||
|
mult[2].size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &mult[0], 3, 0);
|
||
|
|
||
|
} else {
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
p = "this is the answer to the request";
|
||
|
answer.data = p;
|
||
|
answer.size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
}
|
||
|
}
|
||
|
rpt->ret = ret;
|
||
|
rpt->done = 1;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
msg_disp2(dbenv, ch, request, nseg, flags)
|
||
|
DB_ENV *dbenv;
|
||
|
DB_CHANNEL *ch;
|
||
|
DBT *request;
|
||
|
u_int32_t nseg;
|
||
|
u_int32_t flags;
|
||
|
{
|
||
|
CuTest *ct;
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
DBT answer;
|
||
|
const char *p;
|
||
|
char buf[100];
|
||
|
size_t sz;
|
||
|
u_int32_t i;
|
||
|
int ret;
|
||
|
|
||
|
ct = ((struct env_info *)dbenv->app_private)->g->test;
|
||
|
CuAssertTrue(ct, nseg < MAX_SEGS);
|
||
|
for (i = 0; i < nseg; i++) {
|
||
|
if ((sz = (rpt->dbt[rpt->dbt_count].size = request[i].size)) > 0) {
|
||
|
CuAssertTrue(ct, (rpt->dbt[rpt->dbt_count].data = malloc(sz)) != NULL);
|
||
|
memcpy(rpt->dbt[rpt->dbt_count].data,
|
||
|
request[i].data, sz);
|
||
|
} else
|
||
|
rpt->dbt[rpt->dbt_count].data = NULL;
|
||
|
rpt->dbt_count++;
|
||
|
}
|
||
|
|
||
|
if (flags & DB_REPMGR_NEED_RESPONSE) {
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
dbenv->get_errpfx(dbenv, &p);
|
||
|
strncpy(buf, p, sizeof(buf));
|
||
|
answer.data = buf;
|
||
|
answer.size = (u_int32_t)strlen(p) + 1;
|
||
|
if (answer.size > sizeof(buf))
|
||
|
answer.size = sizeof(buf);
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
}
|
||
|
rpt->ret = ret;
|
||
|
rpt->done = 1;
|
||
|
}
|
||
|
|
||
|
/* Test async replies to (async) messages. */
|
||
|
static void
|
||
|
msg_disp3(dbenv, ch, request, nseg, flags)
|
||
|
DB_ENV *dbenv;
|
||
|
DB_CHANNEL *ch;
|
||
|
DBT *request;
|
||
|
u_int32_t nseg;
|
||
|
u_int32_t flags;
|
||
|
{
|
||
|
CuTest *ct;
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
DBT answer;
|
||
|
char *p;
|
||
|
size_t sz;
|
||
|
u_int32_t i;
|
||
|
int ret;
|
||
|
|
||
|
ct = ((struct env_info *)dbenv->app_private)->g->test;
|
||
|
CuAssertTrue(ct, nseg < MAX_SEGS);
|
||
|
for (i = 0; i < nseg; i++) {
|
||
|
if ((sz = (rpt->dbt[rpt->dbt_count].size = request[i].size)) > 0) {
|
||
|
CuAssertTrue(ct, (rpt->dbt[rpt->dbt_count].data = malloc(sz)) != NULL);
|
||
|
memcpy(rpt->dbt[rpt->dbt_count].data,
|
||
|
request[i].data, sz);
|
||
|
} else
|
||
|
rpt->dbt[rpt->dbt_count].data = NULL;
|
||
|
rpt->dbt_count++;
|
||
|
}
|
||
|
|
||
|
ret = 0;
|
||
|
|
||
|
// TODO: test that multiple calls to send_msg are not allowed on a request.
|
||
|
CuAssertTrue(ct, !(flags & DB_REPMGR_NEED_RESPONSE));
|
||
|
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
p = "roses may be pink";
|
||
|
answer.data = p;
|
||
|
answer.size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
|
||
|
if (ret == 0) {
|
||
|
p = "I think";
|
||
|
answer.data = p;
|
||
|
answer.size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
}
|
||
|
rpt->ret = ret;
|
||
|
rpt->done = 1;
|
||
|
}
|
||
|
static void
|
||
|
msg_disp4(dbenv, ch, request, nseg, flags)
|
||
|
DB_ENV *dbenv;
|
||
|
DB_CHANNEL *ch;
|
||
|
DBT *request;
|
||
|
u_int32_t nseg;
|
||
|
u_int32_t flags;
|
||
|
{
|
||
|
CuTest *ct;
|
||
|
struct reports *rpts = ((struct env_info *)dbenv->app_private)->rpts;
|
||
|
struct report *rpt;
|
||
|
size_t sz;
|
||
|
u_int32_t i;
|
||
|
|
||
|
ct = ((struct env_info *)dbenv->app_private)->g->test;
|
||
|
LOCK_MUTEX(&rpts->m);
|
||
|
rpt = &rpts->rpt[rpts->count++];
|
||
|
UNLOCK_MUTEX(&rpts->m);
|
||
|
|
||
|
|
||
|
CuAssertTrue(ct, !(flags & DB_REPMGR_NEED_RESPONSE));
|
||
|
CuAssertTrue(ct, nseg < MAX_SEGS);
|
||
|
for (i = 0; i < nseg; i++) {
|
||
|
if ((sz = (rpt->dbt[rpt->dbt_count].size = request[i].size)) > 0) {
|
||
|
CuAssertTrue(ct, (rpt->dbt[rpt->dbt_count].data = malloc(sz)) != NULL);
|
||
|
memcpy(rpt->dbt[rpt->dbt_count].data,
|
||
|
request[i].data, sz);
|
||
|
} else
|
||
|
rpt->dbt[rpt->dbt_count].data = NULL;
|
||
|
rpt->dbt_count++;
|
||
|
}
|
||
|
|
||
|
rpt->done = 1;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
msg_disp5(dbenv, ch, request, nseg, flags)
|
||
|
DB_ENV *dbenv;
|
||
|
DB_CHANNEL *ch;
|
||
|
DBT *request;
|
||
|
u_int32_t nseg;
|
||
|
u_int32_t flags;
|
||
|
{
|
||
|
struct report *rpt = get_rpt(dbenv);
|
||
|
DBT answer;
|
||
|
u_int8_t buf[100];
|
||
|
char *p;
|
||
|
int ret;
|
||
|
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
answer.flags = DB_DBT_USERMEM;
|
||
|
answer.ulen = sizeof(buf);
|
||
|
answer.data = buf;
|
||
|
if ((ret = ch->set_timeout(ch, 45000000)) != EINVAL ||
|
||
|
(ret = ch->close(ch, 0)) != EINVAL)
|
||
|
rpt->ret = ret;
|
||
|
else
|
||
|
rpt->ret = ch->send_request(ch, request, nseg, &answer, 0, 0);
|
||
|
|
||
|
memset(&answer, 0, sizeof(answer));
|
||
|
p = "roses may be pink";
|
||
|
answer.data = p;
|
||
|
answer.size = (u_int32_t)strlen(p) + 1;
|
||
|
ret = ch->send_msg(ch, &answer, 1, 0);
|
||
|
|
||
|
rpt->done = 1;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_data_init(dbt, data)
|
||
|
DBT *dbt;
|
||
|
char *data;
|
||
|
{
|
||
|
memset(dbt, 0, sizeof(*dbt));
|
||
|
dbt->data = data;
|
||
|
dbt->size = (u_int32_t)strlen(data) + 1;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_zeroes(ch, dest, ct)
|
||
|
DB_CHANNEL *ch;
|
||
|
DB_ENV *dest; /* destination env handle */
|
||
|
CuTest *ct;
|
||
|
{
|
||
|
DBT resp;
|
||
|
DBT rdbts[6];
|
||
|
struct report *rpt;
|
||
|
void *pointer, *vp;
|
||
|
size_t sz;
|
||
|
int i, ret;
|
||
|
|
||
|
memset(&resp, 0, sizeof(resp));
|
||
|
resp.flags = DB_DBT_MALLOC;
|
||
|
|
||
|
#define DATA0 "Dear kindly judge, your honor"
|
||
|
#define DATA1 "my parents treat me rough"
|
||
|
#define DATA2 "with all their marijuana"
|
||
|
#define DATA3 "they won't give me a puff"
|
||
|
#define DATA4 "The didn't wanna have me, but somehow I was had"
|
||
|
#define DATA5 "Leapin' lizards, that's why I'm so bad!"
|
||
|
|
||
|
#undef ADD_TEST_DATA
|
||
|
#define ADD_TEST_DATA(x) do { test_data_init(&rdbts[i++], (x)); } while (0);
|
||
|
|
||
|
i = 0;
|
||
|
ADD_TEST_DATA(DATA0);
|
||
|
ADD_TEST_DATA(DATA1);
|
||
|
ADD_TEST_DATA(DATA2);
|
||
|
ADD_TEST_DATA(DATA3);
|
||
|
ADD_TEST_DATA(DATA4);
|
||
|
ADD_TEST_DATA(DATA5);
|
||
|
|
||
|
rpt = get_rpt(dest);
|
||
|
|
||
|
printf("6. send zero-segment request\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 0, &resp, 0, 0)) == 0);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 0);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
check_dbt_string(&resp, "this is the answer to the request");
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
|
||
|
printf("6.a) send request with a zero-length segment (now why would anyone want to do that?)\n");
|
||
|
clear_rpt(dest);
|
||
|
rdbts[1].size = 0;
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 3, &resp, 0, 0)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 3);
|
||
|
check_dbt_string(&rpt->dbt[0], DATA0);
|
||
|
check_dbt_string(&rpt->dbt[1], "");
|
||
|
check_dbt_string(&rpt->dbt[2], DATA2);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
check_dbt_string(&resp, "this is the answer to the request");
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
i = 1; ADD_TEST_DATA(DATA1); /* restore perturbed test data */
|
||
|
|
||
|
printf("6.b) get a zero-length response\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 4, &resp, 0, 0)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 4);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, resp.size == 0);
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
|
||
|
printf("6.c) get a zero-length response (alternate version)\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 5, &resp, 0, 0)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->dbt_count == 5);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, resp.size == 0);
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
|
||
|
printf("6.d) get a zero-length response (DB_MULTIPLE, zero segments)\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 4, &resp, 0, DB_MULTIPLE)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
DB_MULTIPLE_INIT(pointer, &resp);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, pointer == NULL);
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
|
||
|
printf("6.e) get a zero-length response (DB_MULTIPLE, a zero-length segment)\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 5, &resp, 0, DB_MULTIPLE)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
DB_MULTIPLE_INIT(pointer, &resp);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, sz == 0);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, pointer == NULL);
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
|
||
|
printf("6.f) get a zero-length response (DB_MULTIPLE, a zero-length segment in the middle)\n");
|
||
|
clear_rpt(dest);
|
||
|
CuAssertTrue(ct, (ret = ch->send_request(ch, rdbts, 6, &resp, 0, DB_MULTIPLE)) == 0);
|
||
|
await_done(dest);
|
||
|
CuAssertTrue(ct, rpt->msg_count == 0);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
DB_MULTIPLE_INIT(pointer, &resp);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, rpt->ret == 0);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "roses are red") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, sz == 0);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, strcmp((char*)vp, "violets are blue") == 0);
|
||
|
CuAssertTrue(ct, sz == strlen((char*)vp) + 1);
|
||
|
DB_MULTIPLE_NEXT(pointer, &resp, vp, sz);
|
||
|
CuAssertTrue(ct, pointer == NULL);
|
||
|
if (resp.data)
|
||
|
free(resp.data);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compare, but skip over BDB error msg number at beginning of `actual'.
|
||
|
*/
|
||
|
static int
|
||
|
mystrcmp(actual, expected)
|
||
|
char *actual;
|
||
|
const char *expected;
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
for (p = actual; *p != '\0' && !isspace(*p); p++)
|
||
|
;
|
||
|
for (; *p != '\0' && isspace(*p); p++)
|
||
|
;
|
||
|
return (strcmp(p, expected));
|
||
|
}
|