From dda42251532c347af66ccd6f515745ab83eb8bd5 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 20 Aug 2014 19:18:15 +0200 Subject: libctr: rename/move control interface to libosmocore naming scheme --- include/osmocom/ctrl/control_cmd.h | 179 +++++++++++ include/osmocom/ctrl/control_if.h | 29 ++ openbsc/include/openbsc/control_cmd.h | 179 ----------- openbsc/include/openbsc/control_if.h | 29 -- openbsc/src/libctrl/Makefile.am | 7 - openbsc/src/libctrl/control_cmd.c | 491 ----------------------------- openbsc/src/libctrl/control_if.c | 573 ---------------------------------- src/ctrl/Makefile.am | 7 + src/ctrl/control_cmd.c | 491 +++++++++++++++++++++++++++++ src/ctrl/control_if.c | 573 ++++++++++++++++++++++++++++++++++ 10 files changed, 1279 insertions(+), 1279 deletions(-) create mode 100644 include/osmocom/ctrl/control_cmd.h create mode 100644 include/osmocom/ctrl/control_if.h delete mode 100644 openbsc/include/openbsc/control_cmd.h delete mode 100644 openbsc/include/openbsc/control_if.h delete mode 100644 openbsc/src/libctrl/Makefile.am delete mode 100644 openbsc/src/libctrl/control_cmd.c delete mode 100644 openbsc/src/libctrl/control_if.c create mode 100644 src/ctrl/Makefile.am create mode 100644 src/ctrl/control_cmd.c create mode 100644 src/ctrl/control_if.c diff --git a/include/osmocom/ctrl/control_cmd.h b/include/osmocom/ctrl/control_cmd.h new file mode 100644 index 00000000..2e6863a9 --- /dev/null +++ b/include/osmocom/ctrl/control_cmd.h @@ -0,0 +1,179 @@ +#ifndef _CONTROL_CMD_H +#define _CONTROL_CMD_H + +#include +#include +#include + +#include + +#include + +#define CTRL_CMD_ERROR -1 +#define CTRL_CMD_HANDLED 0 +#define CTRL_CMD_REPLY 1 + +struct ctrl_handle; + +enum ctrl_node_type { + CTRL_NODE_ROOT, /* Root elements */ + CTRL_NODE_BTS, /* BTS specific (net.btsN.) */ + CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */ + CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */ + _LAST_CTRL_NODE +}; + +enum ctrl_type { + CTRL_TYPE_UNKNOWN, + CTRL_TYPE_GET, + CTRL_TYPE_SET, + CTRL_TYPE_GET_REPLY, + CTRL_TYPE_SET_REPLY, + CTRL_TYPE_TRAP, + CTRL_TYPE_ERROR +}; + +struct ctrl_connection { + struct llist_head list_entry; + + /* The queue for sending data back */ + struct osmo_wqueue write_queue; + + /* Buffer for partial input data */ + struct msgb *pending_msg; + + /* Callback if the connection was closed */ + void (*closed_cb)(struct ctrl_connection *conn); + + /* Pending commands for this connection */ + struct llist_head cmds; +}; + +struct ctrl_cmd { + struct ctrl_connection *ccon; + enum ctrl_type type; + char *id; + void *node; + char *variable; + char *value; + char *reply; +}; + +struct ctrl_cmd_struct { + int nr_commands; + char **command; +}; + +struct ctrl_cmd_element { + const char *name; + struct ctrl_cmd_struct strcmd; + int (*set)(struct ctrl_cmd *cmd, void *data); + int (*get)(struct ctrl_cmd *cmd, void *data); + int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data); +}; + +struct ctrl_cmd_map { + char *cmd; + enum ctrl_type type; +}; + +int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data); +int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd); +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); +int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg); +struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type); +struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd); + +#define CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_name) \ +static struct ctrl_cmd_element cmd_##cmdname = { \ + .name = cmdstr, \ + .get = &get_##cmdname, \ + .set = &set_##cmdname, \ + .verify = verify_name, \ +} + +#define CTRL_HELPER_GET_INT(cmdname, dtype, element) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *node = cmd->node; \ + cmd->reply = talloc_asprintf(cmd, "%i", node->element); \ + if (!cmd->reply) { \ + cmd->reply = "OOM"; \ + return CTRL_CMD_ERROR; \ + } \ + return CTRL_CMD_REPLY; \ +} +#define CTRL_HELPER_SET_INT(cmdname, dtype, element) \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *node = cmd->node; \ + int tmp = atoi(cmd->value); \ + node->element = tmp; \ + return get_##cmdname(cmd, _data); \ +} +#define CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ +static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \ +{ \ + int tmp = atoi(value); \ + if ((tmp >= min)&&(tmp <= max)) { \ + return 0; \ + } \ + cmd->reply = "Input not within the range"; \ + return -1; \ +} + +#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \ + CTRL_HELPER_GET_INT(cmdname, dtype, element) \ + CTRL_HELPER_SET_INT(cmdname, dtype, element) \ + CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ +CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) + +#define CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *data = cmd->node; \ + cmd->reply = talloc_asprintf(cmd, "%s", data->element); \ + if (!cmd->reply) { \ + cmd->reply = "OOM"; \ + return CTRL_CMD_ERROR; \ + } \ + return CTRL_CMD_REPLY; \ +} +#define CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *data = cmd->node; \ + bsc_replace_string(cmd->node, &data->element, cmd->value); \ + return get_##cmdname(cmd, _data); \ +} +#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \ + CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ + CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ +CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, NULL) + +#define CTRL_CMD_DEFINE(cmdname, cmdstr) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \ +static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \ +CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) + +#define CTRL_CMD_DEFINE_RO(cmdname, cmdstr) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *data) \ +{ \ + cmd->reply = "Read Only attribute"; \ + return CTRL_CMD_ERROR; \ +} \ +static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \ +{ \ + cmd->reply = "Read Only attribute"; \ + return 1; \ +} \ +CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) + +struct gsm_network; + +#endif /* _CONTROL_CMD_H */ diff --git a/include/osmocom/ctrl/control_if.h b/include/osmocom/ctrl/control_if.h new file mode 100644 index 00000000..d103332a --- /dev/null +++ b/include/osmocom/ctrl/control_if.h @@ -0,0 +1,29 @@ +#ifndef _CONTROL_IF_H +#define _CONTROL_IF_H + +#include +#include +#include + +typedef int (*ctrl_cmd_handler)(struct ctrl_cmd *, void *); + +struct ctrl_handle { + struct osmo_fd listen_fd; + struct gsm_network *gsmnet; + + ctrl_cmd_handler handler; + + /* List of control connections */ + struct llist_head ccon_list; +}; + + +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); +struct ctrl_handle *controlif_setup(struct gsm_network *, uint16_t port, + ctrl_cmd_handler handler); +struct ctrl_handle *bsc_controlif_setup(struct gsm_network *gsmnet, uint16_t port); + +int bsc_ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); + +#endif /* _CONTROL_IF_H */ + diff --git a/openbsc/include/openbsc/control_cmd.h b/openbsc/include/openbsc/control_cmd.h deleted file mode 100644 index 2e6863a9..00000000 --- a/openbsc/include/openbsc/control_cmd.h +++ /dev/null @@ -1,179 +0,0 @@ -#ifndef _CONTROL_CMD_H -#define _CONTROL_CMD_H - -#include -#include -#include - -#include - -#include - -#define CTRL_CMD_ERROR -1 -#define CTRL_CMD_HANDLED 0 -#define CTRL_CMD_REPLY 1 - -struct ctrl_handle; - -enum ctrl_node_type { - CTRL_NODE_ROOT, /* Root elements */ - CTRL_NODE_BTS, /* BTS specific (net.btsN.) */ - CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */ - CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */ - _LAST_CTRL_NODE -}; - -enum ctrl_type { - CTRL_TYPE_UNKNOWN, - CTRL_TYPE_GET, - CTRL_TYPE_SET, - CTRL_TYPE_GET_REPLY, - CTRL_TYPE_SET_REPLY, - CTRL_TYPE_TRAP, - CTRL_TYPE_ERROR -}; - -struct ctrl_connection { - struct llist_head list_entry; - - /* The queue for sending data back */ - struct osmo_wqueue write_queue; - - /* Buffer for partial input data */ - struct msgb *pending_msg; - - /* Callback if the connection was closed */ - void (*closed_cb)(struct ctrl_connection *conn); - - /* Pending commands for this connection */ - struct llist_head cmds; -}; - -struct ctrl_cmd { - struct ctrl_connection *ccon; - enum ctrl_type type; - char *id; - void *node; - char *variable; - char *value; - char *reply; -}; - -struct ctrl_cmd_struct { - int nr_commands; - char **command; -}; - -struct ctrl_cmd_element { - const char *name; - struct ctrl_cmd_struct strcmd; - int (*set)(struct ctrl_cmd *cmd, void *data); - int (*get)(struct ctrl_cmd *cmd, void *data); - int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data); -}; - -struct ctrl_cmd_map { - char *cmd; - enum ctrl_type type; -}; - -int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data); -int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd); -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); -int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg); -struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type); -struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd); - -#define CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_name) \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .get = &get_##cmdname, \ - .set = &set_##cmdname, \ - .verify = verify_name, \ -} - -#define CTRL_HELPER_GET_INT(cmdname, dtype, element) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *node = cmd->node; \ - cmd->reply = talloc_asprintf(cmd, "%i", node->element); \ - if (!cmd->reply) { \ - cmd->reply = "OOM"; \ - return CTRL_CMD_ERROR; \ - } \ - return CTRL_CMD_REPLY; \ -} -#define CTRL_HELPER_SET_INT(cmdname, dtype, element) \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *node = cmd->node; \ - int tmp = atoi(cmd->value); \ - node->element = tmp; \ - return get_##cmdname(cmd, _data); \ -} -#define CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ -static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \ -{ \ - int tmp = atoi(value); \ - if ((tmp >= min)&&(tmp <= max)) { \ - return 0; \ - } \ - cmd->reply = "Input not within the range"; \ - return -1; \ -} - -#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \ - CTRL_HELPER_GET_INT(cmdname, dtype, element) \ - CTRL_HELPER_SET_INT(cmdname, dtype, element) \ - CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ -CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) - -#define CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *data = cmd->node; \ - cmd->reply = talloc_asprintf(cmd, "%s", data->element); \ - if (!cmd->reply) { \ - cmd->reply = "OOM"; \ - return CTRL_CMD_ERROR; \ - } \ - return CTRL_CMD_REPLY; \ -} -#define CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *data = cmd->node; \ - bsc_replace_string(cmd->node, &data->element, cmd->value); \ - return get_##cmdname(cmd, _data); \ -} -#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \ - CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ - CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ -CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, NULL) - -#define CTRL_CMD_DEFINE(cmdname, cmdstr) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \ -static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \ -CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) - -#define CTRL_CMD_DEFINE_RO(cmdname, cmdstr) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *data) \ -{ \ - cmd->reply = "Read Only attribute"; \ - return CTRL_CMD_ERROR; \ -} \ -static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \ -{ \ - cmd->reply = "Read Only attribute"; \ - return 1; \ -} \ -CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) - -struct gsm_network; - -#endif /* _CONTROL_CMD_H */ diff --git a/openbsc/include/openbsc/control_if.h b/openbsc/include/openbsc/control_if.h deleted file mode 100644 index d103332a..00000000 --- a/openbsc/include/openbsc/control_if.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _CONTROL_IF_H -#define _CONTROL_IF_H - -#include -#include -#include - -typedef int (*ctrl_cmd_handler)(struct ctrl_cmd *, void *); - -struct ctrl_handle { - struct osmo_fd listen_fd; - struct gsm_network *gsmnet; - - ctrl_cmd_handler handler; - - /* List of control connections */ - struct llist_head ccon_list; -}; - - -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); -struct ctrl_handle *controlif_setup(struct gsm_network *, uint16_t port, - ctrl_cmd_handler handler); -struct ctrl_handle *bsc_controlif_setup(struct gsm_network *gsmnet, uint16_t port); - -int bsc_ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); - -#endif /* _CONTROL_IF_H */ - diff --git a/openbsc/src/libctrl/Makefile.am b/openbsc/src/libctrl/Makefile.am deleted file mode 100644 index 4f039c82..00000000 --- a/openbsc/src/libctrl/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libctrl.a - -libctrl_a_SOURCES = control_if.c control_cmd.c diff --git a/openbsc/src/libctrl/control_cmd.c b/openbsc/src/libctrl/control_cmd.c deleted file mode 100644 index 44cfa485..00000000 --- a/openbsc/src/libctrl/control_cmd.c +++ /dev/null @@ -1,491 +0,0 @@ -/* SNMP-like status interface - * - * (C) 2010-2011 by Daniel Willmann - * (C) 2010-2011 by On-Waves - * - * All Rights Reserved - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -extern vector ctrl_node_vec; - -static struct ctrl_cmd_map ccm[] = { - {"GET", CTRL_TYPE_GET}, - {"SET", CTRL_TYPE_SET}, - {"GET_REPLY", CTRL_TYPE_GET_REPLY}, - {"SET_REPLY", CTRL_TYPE_SET_REPLY}, - {"TRAP", CTRL_TYPE_TRAP}, - {"ERROR", CTRL_TYPE_ERROR}, - {NULL} -}; - -int ctrl_cmd_str2type(char *s) -{ - int i; - for (i=0; ccm[i].cmd != NULL; i++) { - if (strcasecmp(s, ccm[i].cmd) == 0) - return ccm[i].type; - } - return CTRL_TYPE_UNKNOWN; -} - -char *ctrl_cmd_type2str(int type) -{ - int i; - for (i=0; ccm[i].cmd != NULL; i++) { - if (ccm[i].type == type) - return ccm[i].cmd; - } - return NULL; -} - -/* Functions from libosmocom */ -extern vector cmd_make_descvec(const char *string, const char *descstr); - -/* Get the ctrl_cmd_element that matches this command */ -static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node) -{ - int index, j; - const char *desc; - struct ctrl_cmd_element *cmd_el; - struct ctrl_cmd_struct *cmd_desc; - char *str; - - for (index = 0; index < vector_active(node); index++) { - if ((cmd_el = vector_slot(node, index))) { - cmd_desc = &cmd_el->strcmd; - if (cmd_desc->nr_commands > vector_active(vline)) - continue; - for (j =0; j < vector_active(vline); j++) { - str = vector_slot(vline, j); - desc = cmd_desc->command[j]; - if (desc[0] == '*') - return cmd_el; /* Partial match */ - if (strcmp(desc, str) != 0) - break; - } - /* We went through all the elements and all matched */ - if (j == cmd_desc->nr_commands) - return cmd_el; - } - } - - return NULL; -} - -int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data) -{ - int ret = CTRL_CMD_ERROR; - struct ctrl_cmd_element *cmd_el; - - if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) { - command->reply = "Trying to execute something not GET or SET"; - goto out; - } - if ((command->type == CTRL_TYPE_SET) && (!command->value)) { - command->reply = "SET without a value"; - goto out; - } - - if (!vline) - goto out; - - cmd_el = ctrl_cmd_get_element_match(vline, node); - - if (!cmd_el) { - command->reply = "Command not found"; - goto out; - } - - if (command->type == CTRL_TYPE_SET) { - if (!cmd_el->set) { - command->reply = "SET not implemented"; - goto out; - } - if (cmd_el->verify) { - if ((ret = cmd_el->verify(command, command->value, data))) { - ret = CTRL_CMD_ERROR; - /* If verify() set an appropriate error message, don't change it. */ - if (!command->reply) - command->reply = "Value failed verification."; - goto out; - } - } - ret = cmd_el->set(command, data); - goto out; - } else if (command->type == CTRL_TYPE_GET) { - if (!cmd_el->get) { - command->reply = "GET not implemented"; - goto out; - } - ret = cmd_el->get(command, data); - goto out; - } -out: - if (ret == CTRL_CMD_REPLY) { - if (command->type == CTRL_TYPE_SET) { - command->type = CTRL_TYPE_SET_REPLY; - } else if (command->type == CTRL_TYPE_GET) { - command->type = CTRL_TYPE_GET_REPLY; - } - } else if (ret == CTRL_CMD_ERROR) { - command->type = CTRL_TYPE_ERROR; - } - return ret; -} - -static void add_word(struct ctrl_cmd_struct *cmd, - const char *start, const char *end) -{ - if (!cmd->command) { - cmd->command = talloc_zero_array(tall_vty_vec_ctx, - char*, 1); - cmd->nr_commands = 0; - } else { - cmd->command = talloc_realloc(tall_vty_vec_ctx, - cmd->command, char*, - cmd->nr_commands + 1); - } - - cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command, - start, end - start); -} - -static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name) -{ - const char *cur, *word; - - for (cur = name, word = NULL; cur[0] != '\0'; ++cur) { - /* warn about optionals */ - if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') { - LOGP(DCTRL, LOGL_ERROR, - "Optionals are not supported in '%s'\n", name); - goto failure; - } - - if (isspace(cur[0])) { - if (word) { - add_word(cmd, word, cur); - word = NULL; - } - continue; - } - - if (!word) - word = cur; - } - - if (word) - add_word(cmd, word, cur); - - return; -failure: - cmd->nr_commands = 0; - talloc_free(cmd->command); -} - -int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) -{ - vector cmds_vec; - - cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); - - if (!cmds_vec) { - cmds_vec = vector_init(5); - if (!cmds_vec) { - LOGP(DCTRL, LOGL_ERROR, "vector_init failed.\n"); - return -ENOMEM; - } - vector_set_index(ctrl_node_vec, node, cmds_vec); - } - - vector_set(cmds_vec, cmd); - - create_cmd_struct(&cmd->strcmd, cmd->name); - return 0; -} - -struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type) -{ - struct ctrl_cmd *cmd; - - cmd = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd) - return NULL; - - cmd->type = type; - return cmd; -} - -struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd) -{ - struct ctrl_cmd *cmd2; - - cmd2 = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd2) - return NULL; - - cmd2->type = cmd->type; - if (cmd->id) { - cmd2->id = talloc_strdup(cmd2, cmd->id); - if (!cmd2->id) - goto err; - } - if (cmd->variable) { - cmd2->variable = talloc_strdup(cmd2, cmd->variable); - if (!cmd2->variable) - goto err; - } - if (cmd->value) { - cmd2->value = talloc_strdup(cmd2, cmd->value); - if (!cmd2->value) - goto err; - } - if (cmd->reply) { - cmd2->reply = talloc_strdup(cmd2, cmd->reply); - if (!cmd2->reply) - goto err; - } - - return cmd2; -err: - talloc_free(cmd2); - return NULL; -} - -struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg) -{ - char *str, *tmp, *saveptr = NULL; - char *var, *val; - struct ctrl_cmd *cmd; - - cmd = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - /* Make sure input is NULL terminated */ - msgb_put_u8(msg, 0); - str = (char *) msg->l2h; - - tmp = strtok_r(str, " ", &saveptr); - if (!tmp) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Request malformed"; - goto err; - } - - cmd->type = ctrl_cmd_str2type(tmp); - if (cmd->type == CTRL_TYPE_UNKNOWN) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Request type unknown"; - goto err; - } - - tmp = strtok_r(NULL, " ", &saveptr); - - if (!tmp) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Missing ID"; - goto err; - } - cmd->id = talloc_strdup(cmd, tmp); - if (!cmd->id) - goto oom; - - switch (cmd->type) { - case CTRL_TYPE_GET: - var = strtok_r(NULL, " ", &saveptr); - if (!var) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "GET incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "GET Command incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - LOGP(DCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable); - break; - case CTRL_TYPE_SET: - var = strtok_r(NULL, " ", &saveptr); - val = strtok_r(NULL, "\n", &saveptr); - if (!var || !val) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "SET incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "SET Command incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - cmd->value = talloc_strdup(cmd, val); - if (!cmd->variable || !cmd->value) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value); - break; - case CTRL_TYPE_GET_REPLY: - case CTRL_TYPE_SET_REPLY: - case CTRL_TYPE_TRAP: - var = strtok_r(NULL, " ", &saveptr); - val = strtok_r(NULL, " ", &saveptr); - if (!var || !val) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Trap/Reply incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - cmd->reply = talloc_strdup(cmd, val); - if (!cmd->variable || !cmd->reply) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply); - break; - case CTRL_TYPE_ERROR: - var = strtok_r(NULL, "\0", &saveptr); - if (!var) { - cmd->reply = ""; - goto err; - } - cmd->reply = talloc_strdup(cmd, var); - if (!cmd->reply) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply); - break; - case CTRL_TYPE_UNKNOWN: - default: - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Unknown type"; - goto err; - } - - return cmd; -oom: - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "OOM"; -err: - talloc_free(cmd); - return NULL; -} - -struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) -{ - struct msgb *msg; - char *type, *tmp; - - if (!cmd->id) - return NULL; - - msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); - if (!msg) - return NULL; - - type = ctrl_cmd_type2str(cmd->type); - - switch (cmd->type) { - case CTRL_TYPE_GET: - if (!cmd->variable) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_SET: - if (!cmd->variable || !cmd->value) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, - cmd->value); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_GET_REPLY: - case CTRL_TYPE_SET_REPLY: - case CTRL_TYPE_TRAP: - if (!cmd->variable || !cmd->reply) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, - cmd->reply); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_ERROR: - if (!cmd->reply) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, - cmd->reply); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - default: - LOGP(DCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); - goto err; - break; - } - - return msg; - -err: - msgb_free(msg); - return NULL; -} diff --git a/openbsc/src/libctrl/control_if.c b/openbsc/src/libctrl/control_if.c deleted file mode 100644 index ca59d8c6..00000000 --- a/openbsc/src/libctrl/control_if.c +++ /dev/null @@ -1,573 +0,0 @@ -/* SNMP-like status interface - * - * (C) 2010-2011 by Daniel Willmann - * (C) 2010-2011 by On-Waves - * - * All Rights Reserved - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -vector ctrl_node_vec; - -/* Send command to all */ -int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) -{ - struct ctrl_connection *ccon; - int ret = 0; - - llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { - if (ccon == cmd->ccon) - continue; - if (ctrl_cmd_send(&ccon->write_queue, cmd)) - ret++; - } - return ret; -} - -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) -{ - int ret; - struct msgb *msg; - - msg = ctrl_cmd_make(cmd); - if (!msg) { - LOGP(DCTRL, LOGL_ERROR, "Could not generate msg\n"); - return -1; - } - - ipaccess_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); - ipaccess_prepend_header(msg, IPAC_PROTO_OSMO); - - ret = osmo_wqueue_enqueue(queue, msg); - if (ret != 0) { - LOGP(DCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); - msgb_free(msg); - } - return ret; -} - -struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd) -{ - struct ctrl_cmd *trap; - - trap = ctrl_cmd_cpy(tall_bsc_ctx, cmd); - if (!trap) - return NULL; - - trap->ccon = cmd->ccon; - trap->type = CTRL_TYPE_TRAP; - return trap; -} - -static void control_close_conn(struct ctrl_connection *ccon) -{ - osmo_wqueue_clear(&ccon->write_queue); - close(ccon->write_queue.bfd.fd); - osmo_fd_unregister(&ccon->write_queue.bfd); - llist_del(&ccon->list_entry); - if (ccon->closed_cb) - ccon->closed_cb(ccon); - msgb_free(ccon->pending_msg); - talloc_free(ccon); -} - -static int handle_control_read(struct osmo_fd * bfd) -{ - int ret = -1; - struct osmo_wqueue *queue; - struct ctrl_connection *ccon; - struct ipaccess_head *iph; - struct ipaccess_head_ext *iph_ext; - struct msgb *msg = NULL; - struct ctrl_cmd *cmd; - struct ctrl_handle *ctrl = bfd->data; - - queue = container_of(bfd, struct osmo_wqueue, bfd); - ccon = container_of(queue, struct ctrl_connection, write_queue); - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &ccon->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) - LOGP(DCTRL, LOGL_INFO, "The control connection was closed\n"); - else - LOGP(DCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); - - goto err; - } - - if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { - LOGP(DCTRL, LOGL_ERROR, "The message is too short.\n"); - goto err; - } - - iph = (struct ipaccess_head *) msg->data; - if (iph->proto != IPAC_PROTO_OSMO) { - LOGP(DCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); - goto err; - } - - iph_ext = (struct ipaccess_head_ext *) iph->data; - if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { - LOGP(DCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); - goto err; - } - - msg->l2h = iph_ext->data; - - cmd = ctrl_cmd_parse(ccon, msg); - - if (cmd) { - cmd->ccon = ccon; - if (ctrl->handler(cmd, ctrl->gsmnet) != CTRL_CMD_HANDLED) { - ctrl_cmd_send(queue, cmd); - talloc_free(cmd); - } - } else { - cmd = talloc_zero(ccon, struct ctrl_cmd); - if (!cmd) - goto err; - LOGP(DCTRL, LOGL_ERROR, "Command parser error.\n"); - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Command parser error."; - ctrl_cmd_send(queue, cmd); - talloc_free(cmd); - } - - msgb_free(msg); - return 0; - -err: - control_close_conn(ccon); - msgb_free(msg); - return ret; -} - -static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - if (rc != msg->len) - LOGP(DCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n"); - - return rc; -} - -static struct ctrl_connection *ctrl_connection_alloc(void *ctx) -{ - struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection); - if (!ccon) - return NULL; - - osmo_wqueue_init(&ccon->write_queue, 100); - /* Error handling here? */ - - INIT_LLIST_HEAD(&ccon->cmds); - return ccon; -} - -static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) -{ - int ret, fd, on; - struct ctrl_handle *ctrl; - struct ctrl_connection *ccon; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - LOGP(DCTRL, LOGL_INFO, "accept()ed new control connection from %s\n", - inet_ntoa(sa.sin_addr)); - - on = 1; - ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (ret != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - close(fd); - return ret; - } - ccon = ctrl_connection_alloc(listen_bfd->data); - if (!ccon) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); - close(fd); - return -1; - } - - ctrl = listen_bfd->data; - ccon->write_queue.bfd.data = ctrl; - ccon->write_queue.bfd.fd = fd; - ccon->write_queue.bfd.when = BSC_FD_READ; - ccon->write_queue.read_cb = handle_control_read; - ccon->write_queue.write_cb = control_write_cb; - - ret = osmo_fd_register(&ccon->write_queue.bfd); - if (ret < 0) { - LOGP(DCTRL, LOGL_ERROR, "Could not register FD.\n"); - close(ccon->write_queue.bfd.fd); - talloc_free(ccon); - } - - llist_add(&ccon->list_entry, &ctrl->ccon_list); - - return ret; -} - -static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv) -{ - if (intv >= RATE_CTR_INTV_NUM) - return 0; - - /* Absolute value */ - if (intv == -1) { - return ctr->current; - } else { - return ctr->intv[intv].rate; - } -} - -static char *get_all_rate_ctr_in_group(const struct rate_ctr_group *ctrg, int intv) -{ - int i; - char *counters = talloc_strdup(tall_bsc_ctx, ""); - if (!counters) - return NULL; - - for (i=0;idesc->num_ctr;i++) { - counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64, - ctrg->desc->group_name_prefix, ctrg->idx, - ctrg->desc->ctr_desc[i].name, - get_rate_ctr_value(&ctrg->ctr[i], intv)); - if (!counters) - return NULL; - } - return counters; -} - -static int get_rate_ctr_group(const char *ctr_group, int intv, struct ctrl_cmd *cmd) -{ - int i; - char *counters; - struct rate_ctr_group *ctrg; - - cmd->reply = talloc_asprintf(cmd, "All counters in group %s", ctr_group); - if (!cmd->reply) - goto oom; - - for (i=0;;i++) { - ctrg = rate_ctr_get_group_by_name_idx(ctr_group, i); - if (!ctrg) - break; - - counters = get_all_rate_ctr_in_group(ctrg, intv); - if (!counters) - goto oom; - - cmd->reply = talloc_asprintf_append(cmd->reply, "%s", counters); - talloc_free(counters); - if (!cmd->reply) - goto oom; - } - - /* We found no counter group by that name */ - if (i == 0) { - cmd->reply = talloc_asprintf(cmd, "No counter group with name %s.", ctr_group); - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; -} - -static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) -{ - char *counters; - - counters = get_all_rate_ctr_in_group(ctrg, intv); - if (!counters) - goto oom; - - cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s", - ctrg->desc->group_name_prefix, ctrg->idx, counters); - talloc_free(counters); - if (!cmd->reply) - goto oom; - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; -} - -/* rate_ctr */ -CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *"); -static int get_rate_ctr(struct ctrl_cmd *cmd, void *data) -{ - int intv; - unsigned int idx; - char *ctr_group, *ctr_idx, *ctr_name, *tmp, *dup, *saveptr, *interval; - struct rate_ctr_group *ctrg; - const struct rate_ctr *ctr; - - dup = talloc_strdup(cmd, cmd->variable); - if (!dup) - goto oom; - - /* Skip over possible prefixes (net.) */ - tmp = strstr(dup, "rate_ctr"); - if (!tmp) { - talloc_free(dup); - cmd->reply = "rate_ctr not a token in rate_ctr command!"; - goto err; - } - - strtok_r(tmp, ".", &saveptr); - interval = strtok_r(NULL, ".", &saveptr); - if (!interval) { - talloc_free(dup); - cmd->reply = "Missing interval."; - goto err; - } - - if (!strcmp(interval, "abs")) { - intv = -1; - } else if (!strcmp(interval, "per_sec")) { - intv = RATE_CTR_INTV_SEC; - } else if (!strcmp(interval, "per_min")) { - intv = RATE_CTR_INTV_MIN; - } else if (!strcmp(interval, "per_hour")) { - intv = RATE_CTR_INTV_HOUR; - } else if (!strcmp(interval, "per_day")) { - intv = RATE_CTR_INTV_DAY; - } else { - talloc_free(dup); - cmd->reply = "Wrong interval."; - goto err; - } - - ctr_group = strtok_r(NULL, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - if (!ctr_group || !tmp) { - talloc_free(dup); - cmd->reply = "Counter group must be of form a.b"; - goto err; - } - ctr_group[strlen(ctr_group)] = '.'; - - ctr_idx = strtok_r(NULL, ".", &saveptr); - if (!ctr_idx) { - talloc_free(dup); - return get_rate_ctr_group(ctr_group, intv, cmd); - } - idx = atoi(ctr_idx); - - ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx); - if (!ctrg) { - talloc_free(dup); - cmd->reply = "Counter group not found."; - goto err; - } - - ctr_name = strtok_r(NULL, "\0", &saveptr); - if (!ctr_name) { - talloc_free(dup); - return get_rate_ctr_group_idx(ctrg, intv, cmd); - } - - ctr = rate_ctr_get_by_name(ctrg, ctr_name); - if (!ctr) { - cmd->reply = "Counter name not found."; - talloc_free(dup); - goto err; - } - - talloc_free(dup); - - cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv)); - if (!cmd->reply) - goto oom; - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM"; -err: - return CTRL_CMD_ERROR; -} - -static int set_rate_ctr(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Can't set rate counter."; - - return CTRL_CMD_ERROR; -} - -static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -/* counter */ -CTRL_CMD_DEFINE(counter, "counter *"); -static int get_counter(struct ctrl_cmd *cmd, void *data) -{ - char *ctr_name, *tmp, *dup, *saveptr; - struct osmo_counter *counter; - - cmd->reply = "OOM"; - dup = talloc_strdup(cmd, cmd->variable); - if (!dup) - goto err; - - - tmp = strstr(dup, "counter"); - if (!tmp) { - talloc_free(dup); - goto err; - } - - strtok_r(tmp, ".", &saveptr); - ctr_name = strtok_r(NULL, "\0", &saveptr); - - if (!ctr_name) - goto err; - - counter = osmo_counter_get_by_name(ctr_name); - if (!counter) { - cmd->reply = "Counter name not found."; - talloc_free(dup); - goto err; - } - - talloc_free(dup); - - cmd->reply = talloc_asprintf(cmd, "%lu", counter->value); - if (!cmd->reply) { - cmd->reply = "OOM"; - goto err; - } - - return CTRL_CMD_REPLY; -err: - return CTRL_CMD_ERROR; -} - -static int set_counter(struct ctrl_cmd *cmd, void *data) -{ - - cmd->reply = "Can't set counter."; - - return CTRL_CMD_ERROR; -} - -static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port, - ctrl_cmd_handler handler) -{ - int ret; - struct ctrl_handle *ctrl; - - ctrl = talloc_zero(tall_bsc_ctx, struct ctrl_handle); - if (!ctrl) - return NULL; - - INIT_LLIST_HEAD(&ctrl->ccon_list); - - ctrl->gsmnet = gsmnet; - ctrl->handler = handler; - - ctrl_node_vec = vector_init(5); - if (!ctrl_node_vec) - goto err; - - /* Listen for control connections */ - ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, INADDR_LOOPBACK, port, - 0, listen_fd_cb, ctrl); - if (ret < 0) - goto err_vec; - - ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); - if (ret) - goto err_vec; - ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); - if (ret) - goto err_vec; - - return ctrl; -err_vec: - vector_free(ctrl_node_vec); - ctrl_node_vec = NULL; -err: - talloc_free(ctrl); - return NULL; -} diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am new file mode 100644 index 00000000..4f039c82 --- /dev/null +++ b/src/ctrl/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libctrl.a + +libctrl_a_SOURCES = control_if.c control_cmd.c diff --git a/src/ctrl/control_cmd.c b/src/ctrl/control_cmd.c new file mode 100644 index 00000000..44cfa485 --- /dev/null +++ b/src/ctrl/control_cmd.c @@ -0,0 +1,491 @@ +/* SNMP-like status interface + * + * (C) 2010-2011 by Daniel Willmann + * (C) 2010-2011 by On-Waves + * + * All Rights Reserved + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +extern vector ctrl_node_vec; + +static struct ctrl_cmd_map ccm[] = { + {"GET", CTRL_TYPE_GET}, + {"SET", CTRL_TYPE_SET}, + {"GET_REPLY", CTRL_TYPE_GET_REPLY}, + {"SET_REPLY", CTRL_TYPE_SET_REPLY}, + {"TRAP", CTRL_TYPE_TRAP}, + {"ERROR", CTRL_TYPE_ERROR}, + {NULL} +}; + +int ctrl_cmd_str2type(char *s) +{ + int i; + for (i=0; ccm[i].cmd != NULL; i++) { + if (strcasecmp(s, ccm[i].cmd) == 0) + return ccm[i].type; + } + return CTRL_TYPE_UNKNOWN; +} + +char *ctrl_cmd_type2str(int type) +{ + int i; + for (i=0; ccm[i].cmd != NULL; i++) { + if (ccm[i].type == type) + return ccm[i].cmd; + } + return NULL; +} + +/* Functions from libosmocom */ +extern vector cmd_make_descvec(const char *string, const char *descstr); + +/* Get the ctrl_cmd_element that matches this command */ +static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node) +{ + int index, j; + const char *desc; + struct ctrl_cmd_element *cmd_el; + struct ctrl_cmd_struct *cmd_desc; + char *str; + + for (index = 0; index < vector_active(node); index++) { + if ((cmd_el = vector_slot(node, index))) { + cmd_desc = &cmd_el->strcmd; + if (cmd_desc->nr_commands > vector_active(vline)) + continue; + for (j =0; j < vector_active(vline); j++) { + str = vector_slot(vline, j); + desc = cmd_desc->command[j]; + if (desc[0] == '*') + return cmd_el; /* Partial match */ + if (strcmp(desc, str) != 0) + break; + } + /* We went through all the elements and all matched */ + if (j == cmd_desc->nr_commands) + return cmd_el; + } + } + + return NULL; +} + +int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data) +{ + int ret = CTRL_CMD_ERROR; + struct ctrl_cmd_element *cmd_el; + + if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) { + command->reply = "Trying to execute something not GET or SET"; + goto out; + } + if ((command->type == CTRL_TYPE_SET) && (!command->value)) { + command->reply = "SET without a value"; + goto out; + } + + if (!vline) + goto out; + + cmd_el = ctrl_cmd_get_element_match(vline, node); + + if (!cmd_el) { + command->reply = "Command not found"; + goto out; + } + + if (command->type == CTRL_TYPE_SET) { + if (!cmd_el->set) { + command->reply = "SET not implemented"; + goto out; + } + if (cmd_el->verify) { + if ((ret = cmd_el->verify(command, command->value, data))) { + ret = CTRL_CMD_ERROR; + /* If verify() set an appropriate error message, don't change it. */ + if (!command->reply) + command->reply = "Value failed verification."; + goto out; + } + } + ret = cmd_el->set(command, data); + goto out; + } else if (command->type == CTRL_TYPE_GET) { + if (!cmd_el->get) { + command->reply = "GET not implemented"; + goto out; + } + ret = cmd_el->get(command, data); + goto out; + } +out: + if (ret == CTRL_CMD_REPLY) { + if (command->type == CTRL_TYPE_SET) { + command->type = CTRL_TYPE_SET_REPLY; + } else if (command->type == CTRL_TYPE_GET) { + command->type = CTRL_TYPE_GET_REPLY; + } + } else if (ret == CTRL_CMD_ERROR) { + command->type = CTRL_TYPE_ERROR; + } + return ret; +} + +static void add_word(struct ctrl_cmd_struct *cmd, + const char *start, const char *end) +{ + if (!cmd->command) { + cmd->command = talloc_zero_array(tall_vty_vec_ctx, + char*, 1); + cmd->nr_commands = 0; + } else { + cmd->command = talloc_realloc(tall_vty_vec_ctx, + cmd->command, char*, + cmd->nr_commands + 1); + } + + cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command, + start, end - start); +} + +static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name) +{ + const char *cur, *word; + + for (cur = name, word = NULL; cur[0] != '\0'; ++cur) { + /* warn about optionals */ + if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') { + LOGP(DCTRL, LOGL_ERROR, + "Optionals are not supported in '%s'\n", name); + goto failure; + } + + if (isspace(cur[0])) { + if (word) { + add_word(cmd, word, cur); + word = NULL; + } + continue; + } + + if (!word) + word = cur; + } + + if (word) + add_word(cmd, word, cur); + + return; +failure: + cmd->nr_commands = 0; + talloc_free(cmd->command); +} + +int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) +{ + vector cmds_vec; + + cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); + + if (!cmds_vec) { + cmds_vec = vector_init(5); + if (!cmds_vec) { + LOGP(DCTRL, LOGL_ERROR, "vector_init failed.\n"); + return -ENOMEM; + } + vector_set_index(ctrl_node_vec, node, cmds_vec); + } + + vector_set(cmds_vec, cmd); + + create_cmd_struct(&cmd->strcmd, cmd->name); + return 0; +} + +struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type) +{ + struct ctrl_cmd *cmd; + + cmd = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd) + return NULL; + + cmd->type = type; + return cmd; +} + +struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd) +{ + struct ctrl_cmd *cmd2; + + cmd2 = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd2) + return NULL; + + cmd2->type = cmd->type; + if (cmd->id) { + cmd2->id = talloc_strdup(cmd2, cmd->id); + if (!cmd2->id) + goto err; + } + if (cmd->variable) { + cmd2->variable = talloc_strdup(cmd2, cmd->variable); + if (!cmd2->variable) + goto err; + } + if (cmd->value) { + cmd2->value = talloc_strdup(cmd2, cmd->value); + if (!cmd2->value) + goto err; + } + if (cmd->reply) { + cmd2->reply = talloc_strdup(cmd2, cmd->reply); + if (!cmd2->reply) + goto err; + } + + return cmd2; +err: + talloc_free(cmd2); + return NULL; +} + +struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg) +{ + char *str, *tmp, *saveptr = NULL; + char *var, *val; + struct ctrl_cmd *cmd; + + cmd = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + /* Make sure input is NULL terminated */ + msgb_put_u8(msg, 0); + str = (char *) msg->l2h; + + tmp = strtok_r(str, " ", &saveptr); + if (!tmp) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Request malformed"; + goto err; + } + + cmd->type = ctrl_cmd_str2type(tmp); + if (cmd->type == CTRL_TYPE_UNKNOWN) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Request type unknown"; + goto err; + } + + tmp = strtok_r(NULL, " ", &saveptr); + + if (!tmp) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Missing ID"; + goto err; + } + cmd->id = talloc_strdup(cmd, tmp); + if (!cmd->id) + goto oom; + + switch (cmd->type) { + case CTRL_TYPE_GET: + var = strtok_r(NULL, " ", &saveptr); + if (!var) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "GET incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "GET Command incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + LOGP(DCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable); + break; + case CTRL_TYPE_SET: + var = strtok_r(NULL, " ", &saveptr); + val = strtok_r(NULL, "\n", &saveptr); + if (!var || !val) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "SET incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "SET Command incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + cmd->value = talloc_strdup(cmd, val); + if (!cmd->variable || !cmd->value) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value); + break; + case CTRL_TYPE_GET_REPLY: + case CTRL_TYPE_SET_REPLY: + case CTRL_TYPE_TRAP: + var = strtok_r(NULL, " ", &saveptr); + val = strtok_r(NULL, " ", &saveptr); + if (!var || !val) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Trap/Reply incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + cmd->reply = talloc_strdup(cmd, val); + if (!cmd->variable || !cmd->reply) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply); + break; + case CTRL_TYPE_ERROR: + var = strtok_r(NULL, "\0", &saveptr); + if (!var) { + cmd->reply = ""; + goto err; + } + cmd->reply = talloc_strdup(cmd, var); + if (!cmd->reply) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply); + break; + case CTRL_TYPE_UNKNOWN: + default: + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Unknown type"; + goto err; + } + + return cmd; +oom: + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "OOM"; +err: + talloc_free(cmd); + return NULL; +} + +struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) +{ + struct msgb *msg; + char *type, *tmp; + + if (!cmd->id) + return NULL; + + msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); + if (!msg) + return NULL; + + type = ctrl_cmd_type2str(cmd->type); + + switch (cmd->type) { + case CTRL_TYPE_GET: + if (!cmd->variable) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_SET: + if (!cmd->variable || !cmd->value) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, + cmd->value); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_GET_REPLY: + case CTRL_TYPE_SET_REPLY: + case CTRL_TYPE_TRAP: + if (!cmd->variable || !cmd->reply) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, + cmd->reply); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_ERROR: + if (!cmd->reply) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, + cmd->reply); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + default: + LOGP(DCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); + goto err; + break; + } + + return msg; + +err: + msgb_free(msg); + return NULL; +} diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c new file mode 100644 index 00000000..ca59d8c6 --- /dev/null +++ b/src/ctrl/control_if.c @@ -0,0 +1,573 @@ +/* SNMP-like status interface + * + * (C) 2010-2011 by Daniel Willmann + * (C) 2010-2011 by On-Waves + * + * All Rights Reserved + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +vector ctrl_node_vec; + +/* Send command to all */ +int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) +{ + struct ctrl_connection *ccon; + int ret = 0; + + llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { + if (ccon == cmd->ccon) + continue; + if (ctrl_cmd_send(&ccon->write_queue, cmd)) + ret++; + } + return ret; +} + +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) +{ + int ret; + struct msgb *msg; + + msg = ctrl_cmd_make(cmd); + if (!msg) { + LOGP(DCTRL, LOGL_ERROR, "Could not generate msg\n"); + return -1; + } + + ipaccess_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); + ipaccess_prepend_header(msg, IPAC_PROTO_OSMO); + + ret = osmo_wqueue_enqueue(queue, msg); + if (ret != 0) { + LOGP(DCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); + msgb_free(msg); + } + return ret; +} + +struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd) +{ + struct ctrl_cmd *trap; + + trap = ctrl_cmd_cpy(tall_bsc_ctx, cmd); + if (!trap) + return NULL; + + trap->ccon = cmd->ccon; + trap->type = CTRL_TYPE_TRAP; + return trap; +} + +static void control_close_conn(struct ctrl_connection *ccon) +{ + osmo_wqueue_clear(&ccon->write_queue); + close(ccon->write_queue.bfd.fd); + osmo_fd_unregister(&ccon->write_queue.bfd); + llist_del(&ccon->list_entry); + if (ccon->closed_cb) + ccon->closed_cb(ccon); + msgb_free(ccon->pending_msg); + talloc_free(ccon); +} + +static int handle_control_read(struct osmo_fd * bfd) +{ + int ret = -1; + struct osmo_wqueue *queue; + struct ctrl_connection *ccon; + struct ipaccess_head *iph; + struct ipaccess_head_ext *iph_ext; + struct msgb *msg = NULL; + struct ctrl_cmd *cmd; + struct ctrl_handle *ctrl = bfd->data; + + queue = container_of(bfd, struct osmo_wqueue, bfd); + ccon = container_of(queue, struct ctrl_connection, write_queue); + + ret = ipa_msg_recv_buffered(bfd->fd, &msg, &ccon->pending_msg); + if (ret <= 0) { + if (ret == -EAGAIN) + return 0; + if (ret == 0) + LOGP(DCTRL, LOGL_INFO, "The control connection was closed\n"); + else + LOGP(DCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); + + goto err; + } + + if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { + LOGP(DCTRL, LOGL_ERROR, "The message is too short.\n"); + goto err; + } + + iph = (struct ipaccess_head *) msg->data; + if (iph->proto != IPAC_PROTO_OSMO) { + LOGP(DCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); + goto err; + } + + iph_ext = (struct ipaccess_head_ext *) iph->data; + if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { + LOGP(DCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); + goto err; + } + + msg->l2h = iph_ext->data; + + cmd = ctrl_cmd_parse(ccon, msg); + + if (cmd) { + cmd->ccon = ccon; + if (ctrl->handler(cmd, ctrl->gsmnet) != CTRL_CMD_HANDLED) { + ctrl_cmd_send(queue, cmd); + talloc_free(cmd); + } + } else { + cmd = talloc_zero(ccon, struct ctrl_cmd); + if (!cmd) + goto err; + LOGP(DCTRL, LOGL_ERROR, "Command parser error.\n"); + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Command parser error."; + ctrl_cmd_send(queue, cmd); + talloc_free(cmd); + } + + msgb_free(msg); + return 0; + +err: + control_close_conn(ccon); + msgb_free(msg); + return ret; +} + +static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) +{ + int rc; + + rc = write(bfd->fd, msg->data, msg->len); + if (rc != msg->len) + LOGP(DCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n"); + + return rc; +} + +static struct ctrl_connection *ctrl_connection_alloc(void *ctx) +{ + struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection); + if (!ccon) + return NULL; + + osmo_wqueue_init(&ccon->write_queue, 100); + /* Error handling here? */ + + INIT_LLIST_HEAD(&ccon->cmds); + return ccon; +} + +static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) +{ + int ret, fd, on; + struct ctrl_handle *ctrl; + struct ctrl_connection *ccon; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + + if (!(what & BSC_FD_READ)) + return 0; + + fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + perror("accept"); + return fd; + } + LOGP(DCTRL, LOGL_INFO, "accept()ed new control connection from %s\n", + inet_ntoa(sa.sin_addr)); + + on = 1; + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (ret != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); + close(fd); + return ret; + } + ccon = ctrl_connection_alloc(listen_bfd->data); + if (!ccon) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); + close(fd); + return -1; + } + + ctrl = listen_bfd->data; + ccon->write_queue.bfd.data = ctrl; + ccon->write_queue.bfd.fd = fd; + ccon->write_queue.bfd.when = BSC_FD_READ; + ccon->write_queue.read_cb = handle_control_read; + ccon->write_queue.write_cb = control_write_cb; + + ret = osmo_fd_register(&ccon->write_queue.bfd); + if (ret < 0) { + LOGP(DCTRL, LOGL_ERROR, "Could not register FD.\n"); + close(ccon->write_queue.bfd.fd); + talloc_free(ccon); + } + + llist_add(&ccon->list_entry, &ctrl->ccon_list); + + return ret; +} + +static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv) +{ + if (intv >= RATE_CTR_INTV_NUM) + return 0; + + /* Absolute value */ + if (intv == -1) { + return ctr->current; + } else { + return ctr->intv[intv].rate; + } +} + +static char *get_all_rate_ctr_in_group(const struct rate_ctr_group *ctrg, int intv) +{ + int i; + char *counters = talloc_strdup(tall_bsc_ctx, ""); + if (!counters) + return NULL; + + for (i=0;idesc->num_ctr;i++) { + counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64, + ctrg->desc->group_name_prefix, ctrg->idx, + ctrg->desc->ctr_desc[i].name, + get_rate_ctr_value(&ctrg->ctr[i], intv)); + if (!counters) + return NULL; + } + return counters; +} + +static int get_rate_ctr_group(const char *ctr_group, int intv, struct ctrl_cmd *cmd) +{ + int i; + char *counters; + struct rate_ctr_group *ctrg; + + cmd->reply = talloc_asprintf(cmd, "All counters in group %s", ctr_group); + if (!cmd->reply) + goto oom; + + for (i=0;;i++) { + ctrg = rate_ctr_get_group_by_name_idx(ctr_group, i); + if (!ctrg) + break; + + counters = get_all_rate_ctr_in_group(ctrg, intv); + if (!counters) + goto oom; + + cmd->reply = talloc_asprintf_append(cmd->reply, "%s", counters); + talloc_free(counters); + if (!cmd->reply) + goto oom; + } + + /* We found no counter group by that name */ + if (i == 0) { + cmd->reply = talloc_asprintf(cmd, "No counter group with name %s.", ctr_group); + return CTRL_CMD_ERROR; + } + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM."; + return CTRL_CMD_ERROR; +} + +static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) +{ + char *counters; + + counters = get_all_rate_ctr_in_group(ctrg, intv); + if (!counters) + goto oom; + + cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s", + ctrg->desc->group_name_prefix, ctrg->idx, counters); + talloc_free(counters); + if (!cmd->reply) + goto oom; + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM."; + return CTRL_CMD_ERROR; +} + +/* rate_ctr */ +CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *"); +static int get_rate_ctr(struct ctrl_cmd *cmd, void *data) +{ + int intv; + unsigned int idx; + char *ctr_group, *ctr_idx, *ctr_name, *tmp, *dup, *saveptr, *interval; + struct rate_ctr_group *ctrg; + const struct rate_ctr *ctr; + + dup = talloc_strdup(cmd, cmd->variable); + if (!dup) + goto oom; + + /* Skip over possible prefixes (net.) */ + tmp = strstr(dup, "rate_ctr"); + if (!tmp) { + talloc_free(dup); + cmd->reply = "rate_ctr not a token in rate_ctr command!"; + goto err; + } + + strtok_r(tmp, ".", &saveptr); + interval = strtok_r(NULL, ".", &saveptr); + if (!interval) { + talloc_free(dup); + cmd->reply = "Missing interval."; + goto err; + } + + if (!strcmp(interval, "abs")) { + intv = -1; + } else if (!strcmp(interval, "per_sec")) { + intv = RATE_CTR_INTV_SEC; + } else if (!strcmp(interval, "per_min")) { + intv = RATE_CTR_INTV_MIN; + } else if (!strcmp(interval, "per_hour")) { + intv = RATE_CTR_INTV_HOUR; + } else if (!strcmp(interval, "per_day")) { + intv = RATE_CTR_INTV_DAY; + } else { + talloc_free(dup); + cmd->reply = "Wrong interval."; + goto err; + } + + ctr_group = strtok_r(NULL, ".", &saveptr); + tmp = strtok_r(NULL, ".", &saveptr); + if (!ctr_group || !tmp) { + talloc_free(dup); + cmd->reply = "Counter group must be of form a.b"; + goto err; + } + ctr_group[strlen(ctr_group)] = '.'; + + ctr_idx = strtok_r(NULL, ".", &saveptr); + if (!ctr_idx) { + talloc_free(dup); + return get_rate_ctr_group(ctr_group, intv, cmd); + } + idx = atoi(ctr_idx); + + ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx); + if (!ctrg) { + talloc_free(dup); + cmd->reply = "Counter group not found."; + goto err; + } + + ctr_name = strtok_r(NULL, "\0", &saveptr); + if (!ctr_name) { + talloc_free(dup); + return get_rate_ctr_group_idx(ctrg, intv, cmd); + } + + ctr = rate_ctr_get_by_name(ctrg, ctr_name); + if (!ctr) { + cmd->reply = "Counter name not found."; + talloc_free(dup); + goto err; + } + + talloc_free(dup); + + cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv)); + if (!cmd->reply) + goto oom; + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM"; +err: + return CTRL_CMD_ERROR; +} + +static int set_rate_ctr(struct ctrl_cmd *cmd, void *data) +{ + cmd->reply = "Can't set rate counter."; + + return CTRL_CMD_ERROR; +} + +static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) +{ + return 0; +} + +/* counter */ +CTRL_CMD_DEFINE(counter, "counter *"); +static int get_counter(struct ctrl_cmd *cmd, void *data) +{ + char *ctr_name, *tmp, *dup, *saveptr; + struct osmo_counter *counter; + + cmd->reply = "OOM"; + dup = talloc_strdup(cmd, cmd->variable); + if (!dup) + goto err; + + + tmp = strstr(dup, "counter"); + if (!tmp) { + talloc_free(dup); + goto err; + } + + strtok_r(tmp, ".", &saveptr); + ctr_name = strtok_r(NULL, "\0", &saveptr); + + if (!ctr_name) + goto err; + + counter = osmo_counter_get_by_name(ctr_name); + if (!counter) { + cmd->reply = "Counter name not found."; + talloc_free(dup); + goto err; + } + + talloc_free(dup); + + cmd->reply = talloc_asprintf(cmd, "%lu", counter->value); + if (!cmd->reply) { + cmd->reply = "OOM"; + goto err; + } + + return CTRL_CMD_REPLY; +err: + return CTRL_CMD_ERROR; +} + +static int set_counter(struct ctrl_cmd *cmd, void *data) +{ + + cmd->reply = "Can't set counter."; + + return CTRL_CMD_ERROR; +} + +static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) +{ + return 0; +} + +struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port, + ctrl_cmd_handler handler) +{ + int ret; + struct ctrl_handle *ctrl; + + ctrl = talloc_zero(tall_bsc_ctx, struct ctrl_handle); + if (!ctrl) + return NULL; + + INIT_LLIST_HEAD(&ctrl->ccon_list); + + ctrl->gsmnet = gsmnet; + ctrl->handler = handler; + + ctrl_node_vec = vector_init(5); + if (!ctrl_node_vec) + goto err; + + /* Listen for control connections */ + ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, INADDR_LOOPBACK, port, + 0, listen_fd_cb, ctrl); + if (ret < 0) + goto err_vec; + + ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); + if (ret) + goto err_vec; + ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); + if (ret) + goto err_vec; + + return ctrl; +err_vec: + vector_free(ctrl_node_vec); + ctrl_node_vec = NULL; +err: + talloc_free(ctrl); + return NULL; +} -- cgit v1.2.3