mirror of https://gerrit.osmocom.org/libosmocore
492 lines
11 KiB
C
492 lines
11 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/ctrl/control_cmd.h>
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/msgb.h>
|
|
#include <osmocom/core/application.h>
|
|
#include <osmocom/gsm/protocol/ipaccess.h>
|
|
#include <osmocom/ctrl/control_if.h>
|
|
|
|
static void check_type(enum ctrl_type c)
|
|
{
|
|
const char *t = get_value_string(ctrl_type_vals, c);
|
|
int v = get_string_value(ctrl_type_vals, t);
|
|
|
|
printf("ctrl type %d is %s ", c, t);
|
|
if (v < 0)
|
|
printf("[PARSE FAILED]\n");
|
|
else
|
|
printf("-> %d %s\n", v, c != v ? "FAIL" : "OK");
|
|
}
|
|
|
|
struct msgb *msgb_from_string(const char *str)
|
|
{
|
|
struct ipaccess_head *iph;
|
|
struct ipaccess_head_ext *ipx;
|
|
char *str_msg;
|
|
size_t len = strlen(str) + 1;
|
|
|
|
struct msgb *msg = msgb_alloc(1024, str);
|
|
|
|
iph = (void*)msgb_put(msg, sizeof(*iph));
|
|
iph->proto = IPAC_PROTO_OSMO;
|
|
|
|
ipx = (void*)msgb_put(msg, sizeof(*ipx));
|
|
ipx->proto = IPAC_PROTO_EXT_CTRL;
|
|
|
|
str_msg = (char*)msgb_put(msg, len);
|
|
msg->l2h = (void*)str_msg;
|
|
osmo_strlcpy(str_msg, str, len);
|
|
|
|
iph->len = msgb_length(msg);
|
|
return msg;
|
|
}
|
|
|
|
static void *ctx = NULL;
|
|
|
|
struct one_test {
|
|
const char *cmd_str;
|
|
struct ctrl_cmd expect_parsed;
|
|
const char *reply_str;
|
|
};
|
|
|
|
void assert_same_str(const char *label, const char *expect, const char *got)
|
|
{
|
|
if ((expect == got) || (expect && got && (strcmp(expect, got) == 0))) {
|
|
printf("%s = '%s'\n", label, osmo_escape_str(got, -1));
|
|
return;
|
|
}
|
|
|
|
printf("MISMATCH for '%s':\ngot: %s\n", label, osmo_escape_str(got, -1));
|
|
printf("expected: %s\n", osmo_escape_str(expect, -1));
|
|
OSMO_ASSERT(expect == got);
|
|
}
|
|
|
|
static void assert_test(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, const struct one_test *t)
|
|
{
|
|
struct ctrl_cmd *cmd;
|
|
bool parse_failed;
|
|
struct msgb *msg = msgb_from_string(t->cmd_str);
|
|
int ctx_size_was;
|
|
|
|
printf("test: '%s'\n", osmo_escape_str(t->cmd_str, -1));
|
|
printf("parsing:\n");
|
|
|
|
cmd = ctrl_cmd_parse3(ctx, msg, &parse_failed);
|
|
OSMO_ASSERT(cmd);
|
|
|
|
if (t->expect_parsed.type != cmd->type) {
|
|
printf("type mismatch: got %s\n", get_value_string(ctrl_type_vals, cmd->type));
|
|
OSMO_ASSERT(t->expect_parsed.type == cmd->type);
|
|
} else {
|
|
printf("type = '%s'%s\n", get_value_string(ctrl_type_vals, cmd->type),
|
|
cmd->type != CTRL_TYPE_ERROR ? "" :
|
|
(parse_failed ? " (parse failure)" : " (error received)"));
|
|
}
|
|
|
|
#define ASSERT_SAME_STR(field) \
|
|
assert_same_str(#field, t->expect_parsed.field, cmd->field)
|
|
|
|
ASSERT_SAME_STR(id);
|
|
if (t->expect_parsed.type != CTRL_TYPE_ERROR) {
|
|
ASSERT_SAME_STR(variable);
|
|
ASSERT_SAME_STR(value);
|
|
}
|
|
ASSERT_SAME_STR(reply);
|
|
|
|
talloc_free(cmd);
|
|
msgb_free(msg);
|
|
|
|
printf("handling:\n");
|
|
|
|
ctx_size_was = talloc_total_size(ctx);
|
|
|
|
msg = msgb_from_string(t->cmd_str);
|
|
ctrl_handle_msg(ctrl, ccon, msg);
|
|
|
|
if (llist_empty(&ccon->write_queue.msg_queue)) {
|
|
if (t->reply_str) {
|
|
printf("Got no reply, but expected \"%s\"\n", osmo_escape_str(t->reply_str, -1));
|
|
OSMO_ASSERT(!t->reply_str);
|
|
}
|
|
} else {
|
|
struct msgb *sent_msg = msgb_dequeue(&ccon->write_queue.msg_queue);
|
|
OSMO_ASSERT(sent_msg);
|
|
|
|
char *strbuf = talloc_size(sent_msg, msgb_l2len(sent_msg) + 1);
|
|
memcpy(strbuf, msgb_l2(sent_msg), msgb_l2len(sent_msg));
|
|
strbuf[msgb_l2len(sent_msg)] = '\0';
|
|
|
|
printf("replied: '%s'\n", osmo_escape_str(strbuf, -1));
|
|
OSMO_ASSERT(t->reply_str);
|
|
OSMO_ASSERT(!strcmp(t->reply_str, strbuf));
|
|
msgb_free(sent_msg);
|
|
}
|
|
osmo_wqueue_clear(&ccon->write_queue);
|
|
|
|
msgb_free(msg);
|
|
|
|
if (talloc_total_size(ctx) != ctx_size_was) {
|
|
printf("mem leak!\n");
|
|
talloc_report_full(ctx, stdout);
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
printf("ok\n");
|
|
}
|
|
|
|
static const struct one_test test_messages_list[] = {
|
|
{ "GET 1 variable",
|
|
{
|
|
.type = CTRL_TYPE_GET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
},
|
|
{ "GET 1 variable\n",
|
|
{
|
|
.type = CTRL_TYPE_GET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
|
|
},
|
|
{ "GET 1 var\ni\nable",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET with trailing characters",
|
|
},
|
|
"ERROR 1 GET with trailing characters",
|
|
},
|
|
{ "GET 1 var\ti\table",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET variable contains invalid characters",
|
|
},
|
|
"ERROR 1 GET variable contains invalid characters",
|
|
},
|
|
{ "GET 1 var\ri\rable",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET variable contains invalid characters",
|
|
},
|
|
"ERROR 1 GET variable contains invalid characters",
|
|
},
|
|
{ "GET 1 variable value",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET with trailing characters",
|
|
},
|
|
"ERROR 1 GET with trailing characters",
|
|
},
|
|
{ "GET 1 variable value\n",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET with trailing characters",
|
|
},
|
|
"ERROR 1 GET with trailing characters",
|
|
},
|
|
{ "GET 1 variable multiple value tokens",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET with trailing characters",
|
|
},
|
|
"ERROR 1 GET with trailing characters",
|
|
},
|
|
{ "GET 1 variable multiple value tokens\n",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "GET with trailing characters",
|
|
},
|
|
"ERROR 1 GET with trailing characters",
|
|
},
|
|
{ "SET 1 variable value",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "value",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
},
|
|
{ "SET 1 variable value\n",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "value",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
},
|
|
{ "SET weird_id variable value",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "err",
|
|
.reply = "Invalid message ID number",
|
|
},
|
|
"ERROR err Invalid message ID number",
|
|
},
|
|
{ "SET weird_id variable value\n",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "err",
|
|
.reply = "Invalid message ID number",
|
|
},
|
|
"ERROR err Invalid message ID number",
|
|
},
|
|
{ "SET 1 variable multiple value tokens",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "multiple value tokens",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
|
|
},
|
|
{ "SET 1 variable multiple value tokens\n",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "multiple value tokens",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
|
|
},
|
|
{ "SET 1 variable value_with_trailing_spaces ",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "value_with_trailing_spaces ",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
},
|
|
{ "SET 1 variable value_with_trailing_spaces \n",
|
|
{
|
|
.type = CTRL_TYPE_SET,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.value = "value_with_trailing_spaces ",
|
|
},
|
|
"ERROR 1 Command not found",
|
|
},
|
|
{ "SET \n special_char_id value",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "err",
|
|
.reply = "Invalid message ID number",
|
|
},
|
|
"ERROR err Invalid message ID number",
|
|
},
|
|
{ "SET \t special_char_id value",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "err",
|
|
.reply = "Invalid message ID number",
|
|
},
|
|
"ERROR err Invalid message ID number",
|
|
},
|
|
{ "GET_REPLY 1 variable OK",
|
|
{
|
|
.type = CTRL_TYPE_GET_REPLY,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.reply = "OK",
|
|
},
|
|
},
|
|
{ "SET_REPLY 1 variable OK",
|
|
{
|
|
.type = CTRL_TYPE_SET_REPLY,
|
|
.id = "1",
|
|
.variable = "variable",
|
|
.reply = "OK",
|
|
},
|
|
},
|
|
{ "ERROR 1 some error message",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "1",
|
|
.reply = "some error message",
|
|
},
|
|
},
|
|
{ "ERROR err some error message",
|
|
{
|
|
.type = CTRL_TYPE_ERROR,
|
|
.id = "err",
|
|
.reply = "some error message",
|
|
},
|
|
},
|
|
};
|
|
|
|
static void test_messages()
|
|
{
|
|
struct ctrl_handle *ctrl;
|
|
struct ctrl_connection *ccon;
|
|
int i;
|
|
|
|
ctrl = ctrl_handle_alloc2(ctx, NULL, NULL, 0);
|
|
ccon = talloc_zero(ctx, struct ctrl_connection);
|
|
|
|
osmo_wqueue_init(&ccon->write_queue, 1);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(test_messages_list); i++)
|
|
assert_test(ctrl, ccon, &test_messages_list[i]);
|
|
|
|
talloc_free(ccon);
|
|
talloc_free(ctrl);
|
|
}
|
|
|
|
CTRL_CMD_DEFINE(test_defer, "test-defer");
|
|
static void ctrl_test_defer_cb(void *data)
|
|
{
|
|
struct ctrl_cmd_def *cd = data;
|
|
struct ctrl_cmd *cmd = cd->cmd;
|
|
static int i = 0;
|
|
printf("%s called\n", __func__);
|
|
|
|
if (ctrl_cmd_def_is_zombie(cd)) {
|
|
printf("Is Zombie\n");
|
|
return;
|
|
}
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "Test Defer #%d", i++);
|
|
|
|
ctrl_cmd_def_send(cd);
|
|
return;
|
|
}
|
|
|
|
static struct ctrl_cmd_def *test_defer_cd = NULL;
|
|
static int get_test_defer(struct ctrl_cmd *cmd, void *data)
|
|
{
|
|
void *ctx = cmd->node;
|
|
printf("%s called\n", __func__);
|
|
|
|
test_defer_cd = ctrl_cmd_def_make(ctx, cmd, NULL, 10);
|
|
|
|
return CTRL_CMD_HANDLED;
|
|
}
|
|
static int set_test_defer(struct ctrl_cmd *cmd, void *data)
|
|
{
|
|
return CTRL_CMD_HANDLED;
|
|
}
|
|
static int verify_test_defer(struct ctrl_cmd *cmd, const char *value, void *data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void test_deferred_cmd()
|
|
{
|
|
struct ctrl_handle *ctrl;
|
|
struct ctrl_connection *ccon;
|
|
struct msgb *msg;
|
|
int result;
|
|
int ctx_size_was;
|
|
int ctx_size_before_defer;
|
|
|
|
printf("\n%s\n", __func__);
|
|
ctx_size_was = talloc_total_size(ctx);
|
|
|
|
ctrl = ctrl_handle_alloc2(ctx, NULL, NULL, 0);
|
|
ccon = talloc_zero(ctx, struct ctrl_connection);
|
|
INIT_LLIST_HEAD(&ccon->def_cmds);
|
|
|
|
osmo_wqueue_init(&ccon->write_queue, 1);
|
|
|
|
ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_test_defer);
|
|
|
|
ctx_size_before_defer = talloc_total_size(ctx);
|
|
|
|
msg = msgb_from_string("GET 123 test-defer");
|
|
|
|
result = ctrl_handle_msg(ctrl, ccon, msg);
|
|
printf("ctrl_handle_msg() returned %d\n", result);
|
|
|
|
OSMO_ASSERT(result == CTRL_CMD_HANDLED);
|
|
talloc_free(msg);
|
|
|
|
/* Expecting a ctrl_cmd_def as well as the cmd to still be allocated */
|
|
if (talloc_total_size(ctx) <= ctx_size_before_defer) {
|
|
printf("deferred command apparently deallocated too soon\n");
|
|
talloc_report_full(ctx, stdout);
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
printf("invoking ctrl_test_defer_cb() asynchronously\n");
|
|
ctrl_test_defer_cb(test_defer_cd);
|
|
|
|
/* simulate sending of the reply */
|
|
osmo_wqueue_clear(&ccon->write_queue);
|
|
|
|
/* And now the deferred cmd should be cleaned up completely. */
|
|
if (talloc_total_size(ctx) != ctx_size_before_defer) {
|
|
printf("mem leak!\n");
|
|
talloc_report_full(ctx, stdout);
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
talloc_free(ccon);
|
|
talloc_free(ctrl);
|
|
|
|
if (talloc_total_size(ctx) != ctx_size_was) {
|
|
printf("mem leak!\n");
|
|
talloc_report_full(ctx, stdout);
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
printf("success\n");
|
|
}
|
|
|
|
static struct log_info_cat test_categories[] = {
|
|
};
|
|
|
|
static struct log_info info = {
|
|
.cat = test_categories,
|
|
.num_cat = ARRAY_SIZE(test_categories),
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
ctx = talloc_named_const(NULL, 1, "ctrl_test");
|
|
osmo_init_logging2(ctx, &info);
|
|
msgb_talloc_ctx_init(ctx, 0);
|
|
|
|
printf("Checking ctrl types...\n");
|
|
|
|
check_type(CTRL_TYPE_UNKNOWN);
|
|
check_type(CTRL_TYPE_GET);
|
|
check_type(CTRL_TYPE_SET);
|
|
check_type(CTRL_TYPE_GET_REPLY);
|
|
check_type(CTRL_TYPE_SET_REPLY);
|
|
check_type(CTRL_TYPE_TRAP);
|
|
check_type(CTRL_TYPE_ERROR);
|
|
check_type(64);
|
|
|
|
test_messages();
|
|
|
|
test_deferred_cmd();
|
|
|
|
/* Expecting root ctx + msgb root ctx + 5 logging elements */
|
|
if (talloc_total_blocks(ctx) != 7) {
|
|
talloc_report_full(ctx, stdout);
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
return 0;
|
|
}
|