diff options
Diffstat (limited to 'src/ctrl')
-rw-r--r-- | src/ctrl/Makefile.am | 8 | ||||
-rw-r--r-- | src/ctrl/control_cmd.c | 106 | ||||
-rw-r--r-- | src/ctrl/control_if.c | 169 | ||||
-rw-r--r-- | src/ctrl/control_vty.c | 21 | ||||
-rw-r--r-- | src/ctrl/libosmoctrl.map | 3 |
5 files changed, 209 insertions, 98 deletions
diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am index ca642869..9d3254c1 100644 --- a/src/ctrl/Makefile.am +++ b/src/ctrl/Makefile.am @@ -1,9 +1,10 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=4:0:4 +LIBVERSION=9:0:9 -AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) if ENABLE_CTRL lib_LTLIBRARIES = libosmoctrl.la @@ -12,7 +13,7 @@ libosmoctrl_la_SOURCES = control_cmd.c control_if.c fsm_ctrl_commands.c libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) -version-info $(LIBVERSION) -no-undefined libosmoctrl_la_LIBADD = $(TALLOC_LIBS) \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ $(top_builddir)/src/vty/libosmovty.la @@ -21,5 +22,6 @@ libosmoctrl_la_SOURCES += control_vty.c endif EXTRA_DIST = libosmoctrl.map +EXTRA_libosmoctrl_la_DEPENDENCIES = libosmoctrl.map endif diff --git a/src/ctrl/control_cmd.c b/src/ctrl/control_cmd.c index 33496bd8..db205510 100644 --- a/src/ctrl/control_cmd.c +++ b/src/ctrl/control_cmd.c @@ -20,10 +20,6 @@ * 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 <ctype.h> @@ -35,6 +31,7 @@ #include <unistd.h> #include <osmocom/ctrl/control_cmd.h> +#include <osmocom/ctrl/control_if.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> @@ -207,13 +204,23 @@ failure: } /*! Install a given command definition at a given CTRL node. - * \param[in] node CTRL node at whihc \a cmd is to be installed + * \param[in] node CTRL node at which \a cmd is to be installed * \param[in] cmd command definition to be installed * \returns 0 on success; negative on error */ int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) { vector cmds_vec; + /* If this assert triggers, it means the program forgot to initialize + * the CTRL interface first by calling ctrl_handle_alloc(2)() directly + * or indirectly through ctrl_interface_setup_dynip(2)() + */ + if (!ctrl_node_vec) { + LOGP(DLCTRL, LOGL_ERROR, + "ctrl_handle must be initialized prior to installing cmds.\n"); + return -ENODEV; + } + cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); if (!cmds_vec) { @@ -516,92 +523,61 @@ err: * \returns callee-allocated message buffer containing the encoded \a cmd; NULL on error */ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) { - struct msgb *msg; + struct msgb *msg = NULL; + char *strbuf; + size_t len; const char *type; - char *tmp; if (!cmd->id) return NULL; - msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); - if (!msg) - return NULL; - type = get_value_string(ctrl_type_vals, 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(DLCTRL, 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); + return NULL; + strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); 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(DLCTRL, 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); + return NULL; + strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, + cmd->variable, cmd->value); 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(DLCTRL, 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); + return NULL; + strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, + cmd->variable, cmd->reply); 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(DLCTRL, 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); + return NULL; + strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->reply); break; default: LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); - goto err; - break; + return NULL; } - return msg; + if (!strbuf) { + LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto ret; + } + len = strlen(strbuf); -err: - msgb_free(msg); - return NULL; + msg = msgb_alloc_headroom(len + 128, 128, "ctrl ERROR command make"); + if (!msg) + goto ret; + msg->l2h = msgb_put(msg, len); + memcpy(msg->l2h, strbuf, len); + +ret: + talloc_free(strbuf); + return msg; } /*! Build a deferred control command state and keep it the per-connection list of deferred commands. @@ -672,7 +648,7 @@ int ctrl_cmd_def_send(struct ctrl_cmd_def *cd) cmd->type = CTRL_TYPE_ERROR; } - rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd); + rc = ctrl_cmd_send2(cmd->ccon, cmd); talloc_free(cmd); llist_del(&cd->list); diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c index ce2e3676..c265c3a9 100644 --- a/src/ctrl/control_if.c +++ b/src/ctrl/control_if.c @@ -20,10 +20,6 @@ * 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 "config.h" @@ -35,6 +31,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <limits.h> #include <arpa/inet.h> @@ -50,9 +47,11 @@ #include <osmocom/ctrl/control_cmd.h> #include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_vty.h> #include <osmocom/core/msgb.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stat_item.h> #include <osmocom/core/select.h> #include <osmocom/core/counter.h> #include <osmocom/core/talloc.h> @@ -82,26 +81,22 @@ static LLIST_HEAD(ctrl_lookup_helpers); * \returns 1 on success; 0 in case of error */ int ctrl_parse_get_num(vector vline, int i, long *num) { - char *token, *tmp; + char *token; + int64_t val; if (i >= vector_active(vline)) return 0; token = vector_slot(vline, i); - errno = 0; - if (token[0] == '\0') - return 0; - - *num = strtol(token, &tmp, 10); - if (tmp[0] != '\0' || errno != 0) + if (osmo_str_to_int64(&val, token, 10, LONG_MIN, LONG_MAX)) return 0; - + *num = (long)val; return 1; } /*! Send a CTRL command to all connections. * \param[in] ctrl global control handle - * \param[in] cmd command to send to all connections in \ctrl + * \param[in] cmd command to send to all connections in ctrl * \returns number of times the command has been sent */ int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) { @@ -111,18 +106,28 @@ int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { if (ccon == cmd->ccon) continue; - if (ctrl_cmd_send(&ccon->write_queue, cmd)) + if (ctrl_cmd_send2(ccon, cmd)) ret++; } return ret; } -/*! Encode a CTRL command and append it to the given write queue +/*! Encode a CTRL command and append it to the given ctrl_connection * \param[inout] queue write queue to which encoded \a cmd shall be appended * \param[in] cmd decoded command representation * \returns 0 in case of success; negative on error */ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) { + struct ctrl_connection *ccon = container_of(queue, struct ctrl_connection, write_queue); + return ctrl_cmd_send2(ccon, cmd); +} + +/*! Encode a CTRL command and append it to the given ctrl_connection + * \param[inout] queue write queue to which encoded \a cmd shall be appended + * \param[in] cmd decoded command representation + * \returns 0 in case of success; negative on error */ +int ctrl_cmd_send2(struct ctrl_connection *ccon, struct ctrl_cmd *cmd) +{ int ret; struct msgb *msg; @@ -135,7 +140,7 @@ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); ipa_prepend_header(msg, IPAC_PROTO_OSMO); - ret = osmo_wqueue_enqueue(queue, msg); + ret = osmo_wqueue_enqueue(&ccon->write_queue, msg); if (ret != 0) { LOGP(DLCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); msgb_free(msg); @@ -221,6 +226,10 @@ int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, if (cmd->type == CTRL_TYPE_SET_REPLY || cmd->type == CTRL_TYPE_GET_REPLY) { + if (ctrl->reply_cb) { + ctrl->reply_cb(ctrl, cmd, data); + return CTRL_CMD_HANDLED; + } if (strncmp(cmd->reply, "OK", 2) == 0) { LOGP(DLCTRL, LOGL_DEBUG, "%s <%s> for %s is OK\n", get_value_string(ctrl_type_vals, cmd->type), @@ -465,7 +474,7 @@ int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, stru send_reply: /* There is a reply or error that should be reported back to the sender. */ - ctrl_cmd_send(&ccon->write_queue, cmd); + ctrl_cmd_send2(ccon, cmd); just_free: talloc_free(cmd); return 0; @@ -485,8 +494,14 @@ static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) control_close_conn(ccon); return -EBADF; } - if (rc != msg->len) + if (rc < 0) { LOGP(DLCTRL, LOGL_ERROR, "Failed to write message to the CTRL connection.\n"); + return 0; + } + if (rc < msg->len) { + msgb_pull(msg, rc); + return -EAGAIN; + } return 0; } @@ -510,6 +525,7 @@ struct ctrl_connection *osmo_ctrl_conn_alloc(void *ctx, void *data) INIT_LLIST_HEAD(&ccon->def_cmds); ccon->write_queue.bfd.data = data; + ccon->write_queue.bfd.fd = -1; ccon->write_queue.write_cb = control_write_cb; ccon->write_queue.read_cb = handle_control_read; @@ -584,12 +600,12 @@ static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv, const c } } -static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) +static int get_rate_ctr_group_idx(struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) { unsigned int i; for (i = 0; i < ctrg->desc->num_ctr; i++) { ctrl_cmd_reply_printf(cmd, "%s %"PRIu64";", ctrg->desc->ctr_desc[i].name, - get_rate_ctr_value(&ctrg->ctr[i], intv, ctrg->desc->group_name_prefix)); + get_rate_ctr_value(rate_ctr_group_get_ctr(ctrg, i), intv, ctrg->desc->group_name_prefix)); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; @@ -718,6 +734,100 @@ static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) return 0; } +/* Expose all stat_item groups on CTRL, as read-only variables of the form: + * stat_item.(last|...).group_name.N.item_name + * stat_item.(last|...).group_name.by_name.group_idx_name.item_name + */ +CTRL_CMD_DEFINE_RO(stat_item, "stat_item *"); +static int get_stat_item(struct ctrl_cmd *cmd, void *data) +{ + char *dup; + char *saveptr; + char *value_type; + char *group_name; + char *group_idx_str; + char *item_name; + char *tmp; + int32_t val; + struct osmo_stat_item_group *statg; + const struct osmo_stat_item *stat_item; + + /* cmd will be freed in control_if.c after handling here, so no need to free the dup string. */ + dup = talloc_strdup(cmd, cmd->variable); + if (!dup) { + cmd->reply = "OOM"; + return CTRL_CMD_ERROR; + } + + /* Split off the first "stat_item." part */ + tmp = strtok_r(dup, ".", &saveptr); + if (!tmp || strcmp(tmp, "stat_item") != 0) + goto format_error; + + /* Split off the "last." part (validated further below) */ + value_type = strtok_r(NULL, ".", &saveptr); + if (!value_type) + goto format_error; + + /* Split off the "group_name." part */ + group_name = strtok_r(NULL, ".", &saveptr); + if (!group_name) + goto format_error; + + /* Split off the "N." part */ + group_idx_str = strtok_r(NULL, ".", &saveptr); + if (!group_idx_str) + goto format_error; + if (strcmp(group_idx_str, "by_name") == 0) { + /* The index is not given by "N" but by "by_name.foo". Get the "foo" idx-name */ + group_idx_str = strtok_r(NULL, ".", &saveptr); + statg = osmo_stat_item_get_group_by_name_idxname(group_name, group_idx_str); + } else { + int idx; + if (osmo_str_to_int(&idx, group_idx_str, 10, 0, INT_MAX)) + goto format_error; + statg = osmo_stat_item_get_group_by_name_idx(group_name, idx); + } + if (!statg) { + cmd->reply = "Stat group with given name and index not found"; + return CTRL_CMD_ERROR; + } + + /* Split off the "item_name" part */ + item_name = strtok_r(NULL, ".", &saveptr); + if (!item_name) + goto format_error; + stat_item = osmo_stat_item_get_by_name(statg, item_name); + if (!stat_item) { + cmd->reply = "No such stat item found"; + return CTRL_CMD_ERROR; + } + + tmp = strtok_r(NULL, "", &saveptr); + if (tmp) { + cmd->reply = "Garbage after stat item name"; + return CTRL_CMD_ERROR; + } + + if (!strcmp(value_type, "last")) + val = osmo_stat_item_get_last(stat_item); + else + goto format_error; + + cmd->reply = talloc_asprintf(cmd, "%"PRIu32, val); + if (!cmd->reply) { + cmd->reply = "OOM"; + return CTRL_CMD_ERROR; + } + + return CTRL_CMD_REPLY; + +format_error: + cmd->reply = "Stat item must be of form stat_item.type.group_name.N.item_name," + " e.g. 'stat_item.last.bsc.0.msc_num:connected'"; + return CTRL_CMD_ERROR; +} + /* counter */ CTRL_CMD_DEFINE(counter, "counter *"); static int get_counter(struct ctrl_cmd *cmd, void *data) @@ -779,7 +889,7 @@ static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port, ctrl_cmd_lookup lookup) { - return ctrl_interface_setup_dynip(data, "127.0.0.1", port, lookup); + return ctrl_interface_setup2(data, port, lookup, 0); } static int ctrl_initialized = 0; @@ -805,6 +915,9 @@ static int ctrl_init(unsigned int node_count) ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); if (ret) goto err_vec; + ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_stat_item); + if (ret) + goto err_vec; ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); if (ret) goto err_vec; @@ -928,12 +1041,24 @@ struct ctrl_handle *ctrl_interface_setup_dynip(void *data, return ctrl_interface_setup_dynip2(data, bind_addr, port, lookup, 0); } +/*! Initializes CTRL interface using the configured bind addr/port. + * \param[in] data Pointer which will be made available to each set_..() get_..() verify_..() control command function + * \param[in] default_port TCP port number to bind to if not explicitly configured + * \param[in] lookup Lookup function pointer, can be NULL + * \param[in] node_count Number of CTRL nodes to allocate, 0 for default. + */ +struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup, + unsigned int node_count) +{ + return ctrl_interface_setup_dynip2(data, ctrl_vty_get_bind_addr(), ctrl_vty_get_bind_port(default_port), lookup, + node_count); +} /*! Install a lookup helper function for control nodes * This function is used by e.g. library code to install lookup helpers * for additional nodes in the control interface. * \param[in] lookup The lookup helper function - * \retuns - on success; negative on error. + * \returns - on success; negative on error. */ int ctrl_lookup_register(ctrl_cmd_lookup lookup) { diff --git a/src/ctrl/control_vty.c b/src/ctrl/control_vty.c index ef988892..a7ebddc2 100644 --- a/src/ctrl/control_vty.c +++ b/src/ctrl/control_vty.c @@ -17,10 +17,6 @@ * 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 <stdlib.h> @@ -30,16 +26,20 @@ static void *ctrl_vty_ctx = NULL; static const char *ctrl_vty_bind_addr = NULL; +/* Port the CTRL should bind to: -1 means not configured */ +static int ctrl_bind_port = -1; DEFUN(cfg_ctrl_bind_addr, cfg_ctrl_bind_addr_cmd, - "bind A.B.C.D", + "bind A.B.C.D [<0-65535>]", "Set bind address to listen for Control connections\n" - "Local IP address (default 127.0.0.1)\n") + "Local IP address (default 127.0.0.1)\n" + "Local TCP port number\n") { talloc_free((char*)ctrl_vty_bind_addr); ctrl_vty_bind_addr = NULL; ctrl_vty_bind_addr = talloc_strdup(ctrl_vty_ctx, argv[0]); + ctrl_bind_port = argc > 1 ? atoi(argv[1]) : -1; return CMD_SUCCESS; } @@ -50,6 +50,11 @@ const char *ctrl_vty_get_bind_addr(void) return ctrl_vty_bind_addr; } +uint16_t ctrl_vty_get_bind_port(uint16_t default_port) +{ + return ctrl_bind_port >= 0 ? ctrl_bind_port : default_port; +} + static struct cmd_node ctrl_node = { L_CTRL_NODE, "%s(config-ctrl)# ", @@ -82,10 +87,10 @@ static int config_write_ctrl(struct vty *vty) int ctrl_vty_init(void *ctx) { ctrl_vty_ctx = ctx; - install_element(CONFIG_NODE, &cfg_ctrl_cmd); + install_lib_element(CONFIG_NODE, &cfg_ctrl_cmd); install_node(&ctrl_node, config_write_ctrl); - install_element(L_CTRL_NODE, &cfg_ctrl_bind_addr_cmd); + install_lib_element(L_CTRL_NODE, &cfg_ctrl_bind_addr_cmd); return 0; } diff --git a/src/ctrl/libosmoctrl.map b/src/ctrl/libosmoctrl.map index f995467b..3418e620 100644 --- a/src/ctrl/libosmoctrl.map +++ b/src/ctrl/libosmoctrl.map @@ -15,6 +15,7 @@ ctrl_cmd_parse; ctrl_cmd_parse2; ctrl_cmd_parse3; ctrl_cmd_send; +ctrl_cmd_send2; ctrl_cmd_send_to_all; ctrl_cmd_send_trap; ctrl_cmd_trap; @@ -22,12 +23,14 @@ ctrl_handle_alloc; /* could be removed? */ ctrl_handle_alloc2; /* could be removed? */ ctrl_handle_msg; /* only used in unit test */ ctrl_interface_setup; +ctrl_interface_setup2; ctrl_interface_setup_dynip; ctrl_interface_setup_dynip2; ctrl_lookup_register; ctrl_parse_get_num; ctrl_type_vals; ctrl_vty_get_bind_addr; +ctrl_vty_get_bind_port; ctrl_vty_init; osmo_ctrl_conn_alloc; |