aboutsummaryrefslogtreecommitdiffstats
path: root/src/ctrl/control_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ctrl/control_if.c')
-rw-r--r--src/ctrl/control_if.c169
1 files changed, 147 insertions, 22 deletions
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)
{