/*
Unix SMB/CIFS implementation.
local test for messaging code
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "includes.h"
#include "lib/events/events.h"
#include "lib/messaging/irpc.h"
#include "torture/torture.h"
#include "cluster/cluster.h"
#include "param/param.h"
#include "torture/local/proto.h"
#include "system/select.h"
#include "system/filesys.h"
#include
#include
static uint32_t msg_pong;
static void ping_message(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id src,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
NTSTATUS status;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
status = imessaging_send(msg, src, msg_pong, data);
if (!NT_STATUS_IS_OK(status)) {
printf("pong failed - %s\n", nt_errstr(status));
}
}
static void pong_message(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id src,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
int *count = (int *)private_data;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
(*count)++;
}
static void exit_message(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id src,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
talloc_free(private_data);
exit(0);
}
/*
test ping speed
*/
static bool test_ping_speed(struct torture_context *tctx)
{
struct tevent_context *ev;
struct imessaging_context *msg_client_ctx;
struct imessaging_context *msg_server_ctx;
int ping_count = 0;
int pong_count = 0;
struct timeval tv;
int timelimit = torture_setting_int(tctx, "timelimit", 10);
uint32_t msg_ping, msg_exit;
lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
ev = tctx->ev;
msg_server_ctx = imessaging_init(tctx,
tctx->lp_ctx, cluster_id(0, 1),
ev);
torture_assert(tctx, msg_server_ctx != NULL, "Failed to init ping messaging context");
imessaging_register_tmp(msg_server_ctx, NULL, ping_message, &msg_ping);
imessaging_register_tmp(msg_server_ctx, tctx, exit_message, &msg_exit);
msg_client_ctx = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 2),
ev);
torture_assert(tctx, msg_client_ctx != NULL,
"msg_client_ctx imessaging_init() failed");
imessaging_register_tmp(msg_client_ctx, &pong_count, pong_message, &msg_pong);
tv = timeval_current();
torture_comment(tctx, "Sending pings for %d seconds\n", timelimit);
while (timeval_elapsed(&tv) < timelimit) {
DATA_BLOB data;
NTSTATUS status1, status2;
data.data = discard_const_p(uint8_t, "testing");
data.length = strlen((const char *)data.data);
status1 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, &data);
status2 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, NULL);
torture_assert_ntstatus_ok(tctx, status1, "msg1 failed");
ping_count++;
torture_assert_ntstatus_ok(tctx, status2, "msg2 failed");
ping_count++;
while (ping_count > pong_count + 20) {
tevent_loop_once(ev);
}
}
torture_comment(tctx, "waiting for %d remaining replies (done %d)\n",
ping_count - pong_count, pong_count);
while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
tevent_loop_once(ev);
}
torture_comment(tctx, "sending exit\n");
imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_exit, NULL);
torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
torture_comment(tctx, "ping rate of %.0f messages/sec\n",
(ping_count+pong_count)/timeval_elapsed(&tv));
talloc_free(msg_client_ctx);
talloc_free(msg_server_ctx);
return true;
}
static bool test_messaging_overflow(struct torture_context *tctx)
{
struct imessaging_context *msg_ctx;
ssize_t nwritten, nread;
pid_t child;
char c = 0;
int up_pipe[2], down_pipe[2];
int i, ret, child_status;
ret = pipe(up_pipe);
torture_assert(tctx, ret == 0, "pipe failed");
ret = pipe(down_pipe);
torture_assert(tctx, ret == 0, "pipe failed");
child = fork();
if (child < 0) {
torture_fail(tctx, "fork failed");
}
if (child == 0) {
ret = tevent_re_initialise(tctx->ev);
torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
cluster_id(getpid(), 0),
tctx->ev);
torture_assert(tctx, msg_ctx != NULL,
"imessaging_init failed");
do {
nwritten = write(up_pipe[1], &c, 1);
} while ((nwritten == -1) && (errno == EINTR));
ret = close(down_pipe[1]);
torture_assert(tctx, ret == 0, "close failed");
do {
nread = read(down_pipe[0], &c, 1);
} while ((nread == -1) && (errno == EINTR));
exit(0);
}
do {
nread = read(up_pipe[0], &c, 1);
} while ((nread == -1) && (errno == EINTR));
msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
tctx->ev);
torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
for (i=0; i<1000; i++) {
NTSTATUS status;
status = imessaging_send(msg_ctx, cluster_id(child, 0),
MSG_PING, NULL);
torture_assert_ntstatus_ok(tctx, status,
"imessaging_send failed");
}
tevent_loop_once(tctx->ev);
talloc_free(msg_ctx);
ret = close(down_pipe[1]);
torture_assert(tctx, ret == 0, "close failed");
ret = waitpid(child, &child_status, 0);
torture_assert(tctx, ret == child, "wrong child exited");
torture_assert(tctx, child_status == 0, "child failed");
poll(NULL, 0, 500);
return true;
}
struct overflow_parent_child {
gnutls_hash_hd_t md5_hash_hnd;
bool done;
};
static void overflow_md5_child_handler(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
struct overflow_parent_child *state = private_data;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
if (data->length == 0) {
state->done = true;
return;
}
gnutls_hash(state->md5_hash_hnd, data->data, data->length);
}
struct overflow_child_parent {
uint8_t final[16];
bool done;
};
static void overflow_md5_parent_handler(struct imessaging_context *msg_ctx,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
struct overflow_child_parent *state = private_data;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
if (data->length != sizeof(state->final)) {
memset(state->final, 0, sizeof(state->final));
state->done = true;
return;
}
memcpy(state->final, data->data, 16);
state->done = true;
}
static bool test_messaging_overflow_check(struct torture_context *tctx)
{
struct imessaging_context *msg_ctx;
ssize_t nwritten, nread;
pid_t child;
char c = 0;
int up_pipe[2], down_pipe[2];
int i, ret, child_status;
gnutls_hash_hd_t hash_hnd;
uint8_t final[16];
struct overflow_child_parent child_msg = { .done = false };
NTSTATUS status;
ret = pipe(up_pipe);
torture_assert(tctx, ret == 0, "pipe failed");
ret = pipe(down_pipe);
torture_assert(tctx, ret == 0, "pipe failed");
child = fork();
if (child < 0) {
torture_fail(tctx, "fork failed");
}
if (child == 0) {
struct overflow_parent_child child_state = { .done = false };
DATA_BLOB retblob = { .data = final, .length = sizeof(final) };
ret = tevent_re_initialise(tctx->ev);
torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
gnutls_hash_init(&child_state.md5_hash_hnd, GNUTLS_DIG_MD5);
msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
cluster_id(getpid(), 0),
tctx->ev);
torture_assert(tctx, msg_ctx != NULL,
"imessaging_init failed");
status = imessaging_register(msg_ctx, &child_state,
MSG_TMP_BASE-1,
overflow_md5_child_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status),
"imessaging_register failed");
do {
nwritten = write(up_pipe[1], &c, 1);
} while ((nwritten == -1) && (errno == EINTR));
ret = close(down_pipe[1]);
torture_assert(tctx, ret == 0, "close failed");
do {
nread = read(down_pipe[0], &c, 1);
} while ((nread == -1) && (errno == EINTR));
while (!child_state.done) {
tevent_loop_once(tctx->ev);
}
gnutls_hash_deinit(child_state.md5_hash_hnd, final);
status = imessaging_send(msg_ctx,
cluster_id(getppid(), 0),
MSG_TMP_BASE-2,
&retblob);
torture_assert(tctx, NT_STATUS_IS_OK(status),
"imessaging_send failed");
exit(0);
}
do {
nread = read(up_pipe[0], &c, 1);
} while ((nread == -1) && (errno == EINTR));
msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
tctx->ev);
torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
status = imessaging_register(msg_ctx,
&child_msg,
MSG_TMP_BASE-2,
overflow_md5_parent_handler);
torture_assert(tctx,
NT_STATUS_IS_OK(status),
"imessaging_register failed");
gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
for (i=0; i<1000; i++) {
size_t len = ((random() % 100) + 1);
uint8_t buf[len];
DATA_BLOB blob = { .data = buf, .length = len };
generate_random_buffer(buf, len);
gnutls_hash(hash_hnd, buf, len);
status = imessaging_send(msg_ctx, cluster_id(child, 0),
MSG_TMP_BASE-1, &blob);
torture_assert_ntstatus_ok(tctx, status,
"imessaging_send failed");
}
status = imessaging_send(msg_ctx, cluster_id(child, 0),
MSG_TMP_BASE-1, NULL);
torture_assert_ntstatus_ok(tctx, status,
"imessaging_send failed");
gnutls_hash_deinit(hash_hnd, final);
do {
nwritten = write(down_pipe[1], &c, 1);
} while ((nwritten == -1) && (errno == EINTR));
while (!child_msg.done) {
tevent_loop_once(tctx->ev);
}
ret = close(down_pipe[1]);
torture_assert(tctx, ret == 0, "close failed");
talloc_free(msg_ctx);
ret = waitpid(child, &child_status, 0);
torture_assert(tctx, ret == child, "wrong child exited");
torture_assert(tctx, child_status == 0, "child failed");
if (memcmp(final, child_msg.final, 16) != 0) {
dump_data_file(final, 16, false, stderr);
dump_data_file(child_msg.final, 16, false, stderr);
fflush(stderr);
torture_fail(tctx, "checksum comparison failed");
}
return true;
}
struct test_multi_ctx {
struct torture_context *tctx;
struct imessaging_context *server_ctx;
struct imessaging_context *client_ctx[4];
size_t num_missing;
bool got_server;
bool got_client_0_1;
bool got_client_2_3;
bool ok;
};
static void multi_ctx_server_handler(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
struct test_multi_ctx *state = private_data;
char *str = NULL;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
torture_assert_goto(state->tctx, state->num_missing >= 1,
state->ok, fail,
"num_missing should be at least 1.");
state->num_missing -= 1;
torture_assert_goto(state->tctx, !state->got_server,
state->ok, fail,
"already got server.");
state->got_server = true;
/*
* We free the context itself and most likely reuse
* the memory immediately.
*/
TALLOC_FREE(state->server_ctx);
str = generate_random_str(state->tctx, 128);
torture_assert_goto(state->tctx, str != NULL,
state->ok, fail,
"generate_random_str()");
fail:
return;
}
static void multi_ctx_client_0_1_handler(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
struct test_multi_ctx *state = private_data;
char *str = NULL;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
torture_assert_goto(state->tctx, state->num_missing >= 2,
state->ok, fail,
"num_missing should be at least 2.");
state->num_missing -= 2;
torture_assert_goto(state->tctx, !state->got_client_0_1,
state->ok, fail,
"already got client_0_1.");
state->got_client_0_1 = true;
/*
* We free two contexts and most likely reuse
* the memory immediately.
*/
TALLOC_FREE(state->client_ctx[0]);
str = generate_random_str(state->tctx, 128);
torture_assert_goto(state->tctx, str != NULL,
state->ok, fail,
"generate_random_str()");
TALLOC_FREE(state->client_ctx[1]);
str = generate_random_str(state->tctx, 128);
torture_assert_goto(state->tctx, str != NULL,
state->ok, fail,
"generate_random_str()");
fail:
return;
}
static void multi_ctx_client_2_3_handler(struct imessaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
size_t num_fds,
int *fds,
DATA_BLOB *data)
{
struct test_multi_ctx *state = private_data;
char *str = NULL;
if (num_fds != 0) {
DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
return;
}
torture_assert_goto(state->tctx, state->num_missing >= 2,
state->ok, fail,
"num_missing should be at least 2.");
state->num_missing -= 2;
torture_assert_goto(state->tctx, !state->got_client_2_3,
state->ok, fail,
"already got client_2_3.");
state->got_client_2_3 = true;
/*
* We free two contexts and most likely reuse
* the memory immediately.
*/
TALLOC_FREE(state->client_ctx[2]);
str = generate_random_str(state->tctx, 128);
torture_assert_goto(state->tctx, str != NULL,
state->ok, fail,
"generate_random_str()");
TALLOC_FREE(state->client_ctx[3]);
str = generate_random_str(state->tctx, 128);
torture_assert_goto(state->tctx, str != NULL,
state->ok, fail,
"generate_random_str()");
fail:
return;
}
static bool test_multi_ctx(struct torture_context *tctx)
{
struct test_multi_ctx state = {
.tctx = tctx,
.ok = true,
};
struct timeval tv;
NTSTATUS status;
lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
/*
* We use cluster_id(0, 0) as that gets for
* all task ids.
*/
state.server_ctx = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 0),
tctx->ev);
torture_assert(tctx, state.server_ctx != NULL,
"Failed to init messaging context");
status = imessaging_register(state.server_ctx, &state,
MSG_TMP_BASE-1,
multi_ctx_server_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
state.client_ctx[0] = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 0),
tctx->ev);
torture_assert(tctx, state.client_ctx[0] != NULL,
"msg_client_ctx imessaging_init() failed");
status = imessaging_register(state.client_ctx[0], &state,
MSG_TMP_BASE-1,
multi_ctx_client_0_1_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
state.client_ctx[1] = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 0),
tctx->ev);
torture_assert(tctx, state.client_ctx[1] != NULL,
"msg_client_ctx imessaging_init() failed");
status = imessaging_register(state.client_ctx[1], &state,
MSG_TMP_BASE-1,
multi_ctx_client_0_1_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
state.client_ctx[2] = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 0),
tctx->ev);
torture_assert(tctx, state.client_ctx[2] != NULL,
"msg_client_ctx imessaging_init() failed");
status = imessaging_register(state.client_ctx[2], &state,
MSG_TMP_BASE-1,
multi_ctx_client_2_3_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
state.client_ctx[3] = imessaging_init(tctx,
tctx->lp_ctx,
cluster_id(0, 0),
tctx->ev);
torture_assert(tctx, state.client_ctx[3] != NULL,
"msg_client_ctx imessaging_init() failed");
status = imessaging_register(state.client_ctx[3], &state,
MSG_TMP_BASE-1,
multi_ctx_client_2_3_handler);
torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
/*
* Send one message that need to arrive on 3 ( 5 - 2 ) handlers.
*/
state.num_missing = 5;
status = imessaging_send(state.server_ctx,
cluster_id(0, 0),
MSG_TMP_BASE-1, NULL);
torture_assert_ntstatus_ok(tctx, status, "msg failed");
tv = timeval_current();
while (timeval_elapsed(&tv) < 30 && state.num_missing > 0 && state.ok) {
int ret;
ret = tevent_loop_once(tctx->ev);
torture_assert_int_equal(tctx, ret, 0, "tevent_loop_once()");
}
if (!state.ok) {
return false;
}
torture_assert_int_equal(tctx, state.num_missing, 0,
"wrong message count");
torture_assert(tctx, state.got_client_0_1, "got_client_0_1");
torture_assert(tctx, state.got_client_2_3, "got_client_2_3");
torture_assert(tctx, state.got_server, "got_server");
return true;
}
struct torture_suite *torture_local_messaging(TALLOC_CTX *mem_ctx)
{
struct torture_suite *s = torture_suite_create(mem_ctx, "messaging");
torture_suite_add_simple_test(s, "overflow", test_messaging_overflow);
torture_suite_add_simple_test(s, "overflow_check",
test_messaging_overflow_check);
torture_suite_add_simple_test(s, "ping_speed", test_ping_speed);
torture_suite_add_simple_test(s, "multi_ctx", test_multi_ctx);
return s;
}