summaryrefslogtreecommitdiffstats
path: root/openbsc/src/libctrl/control_cmd.c
diff options
context:
space:
mode:
authorDaniel Willmann <daniel@totalueberwachung.de>2010-10-21 15:00:36 +0200
committerHarald Welte <laforge@gnumonks.org>2011-07-13 14:06:18 +0200
commit4462f8c30f744406dd7609eebf7ca1878003e14d (patch)
treec5547611355116d8ab91989b30b67e6c275ecc4a /openbsc/src/libctrl/control_cmd.c
parent203d86531764d680cd73b71878263e29d1603a04 (diff)
Add libctrl, an SNMP-like control interface
In contrast to the VTY interface the control interface is meant to be used by programs. This patch adds basic support, no commands are defined.
Diffstat (limited to 'openbsc/src/libctrl/control_cmd.c')
-rw-r--r--openbsc/src/libctrl/control_cmd.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/openbsc/src/libctrl/control_cmd.c b/openbsc/src/libctrl/control_cmd.c
new file mode 100644
index 000000000..06af3b320
--- /dev/null
+++ b/openbsc/src/libctrl/control_cmd.c
@@ -0,0 +1,443 @@
+/* SNMP-like status interface
+ *
+ * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (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 <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <openbsc/control_cmd.h>
+#include <openbsc/debug.h>
+#include <openbsc/vty.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vector.h>
+
+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;
+ command->reply = "Value failed verification.";
+ goto out;
+ }
+ } else if (cmd_el->param) {
+ LOGP(DINP, LOGL_NOTICE, "Parameter verification unimplemented, continuing without\n");
+ }
+ 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(DINP, 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(DINP, 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_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(DINP, 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(DINP, LOGL_NOTICE, "GET Command incomplete\n");
+ goto err;
+ }
+ cmd->variable = talloc_strdup(cmd, var);
+ LOGP(DINP, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
+ break;
+ case CTRL_TYPE_SET:
+ var = strtok_r(NULL, " ", &saveptr);
+ val = strtok_r(NULL, " ", &saveptr);
+ if (!var || !val) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "SET incomplete";
+ LOGP(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, 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(DINP, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
+ goto err;
+ break;
+ }
+
+ return msg;
+
+err:
+ msgb_free(msg);
+ return NULL;
+}