initial checkin
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..aad62b5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+LDFLAGS:=-losmocore -losmogsm -ltalloc
+CFLAGS:=-Wall
+
+osmo-ctrl-client: osmo-ctrl-client.o simple_ctrl.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+clean:
+ @rm osmo-ctrl-client *.o
diff --git a/osmo-ctrl-client.c b/osmo-ctrl-client.c
new file mode 100644
index 0000000..463bffe
--- /dev/null
+++ b/osmo-ctrl-client.c
@@ -0,0 +1,77 @@
+/* Simple command-line client against the Osmocom CTRL interface */
+
+/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "simple_ctrl.h"
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+static struct log_info log_info = {};
+
+static void exit_help(void)
+{
+ printf("help!\n");
+ exit(2);
+}
+
+int main(int argc, char **argv)
+{
+ struct simple_ctrl_handle *sch;
+ const char *host;
+ uint16_t port;
+ int rc;
+
+ if (argc < 4)
+ exit_help();
+
+ host = argv[1];
+ port = atoi(argv[2]);
+
+ osmo_init_logging2(NULL, &log_info);
+
+ sch = simple_ctrl_open(NULL, host, port);
+ if (!sch)
+ exit(1);
+
+ if (!strcmp(argv[3], "get")) {
+ char *val;
+ if (argc < 5)
+ exit_help();
+ val = simple_ctrl_get(sch, argv[4]);
+ if (!val)
+ exit(2);
+ printf("%s\n", val);
+ } else if (!strcmp(argv[3], "set")) {
+ if (argc < 6)
+ exit_help();
+ rc = simple_ctrl_set(sch, argv[4], argv[5]);
+ if (rc < 0)
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/osysmon.h b/osysmon.h
new file mode 100644
index 0000000..d9d1a11
--- /dev/null
+++ b/osysmon.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#include <stdbool.h>
+#include <linux/if.h>
+
+/* information gathered from sysinfo(2) */
+struct osysmon_state_sysinfo {
+ unsigned long uptime_secs;
+ unsigned long loads[3];
+ unsigned long freeram;
+};
+
+/* one network interface */
+struct osysmon_state_iface {
+ char name[IFNAMSIZ+1];
+ uint32_t flags; /* enum net_device_flags IFF_* */
+ uint8_t hwasddr[6];
+ uint32_t ipaddr;
+};
+
+/* information gathered from RTNL via libmnl */
+struct osysmon_state_rtnl {
+ struct osysmon_state_iface iface[8];
+ unsigned int num_iface;
+ struct {
+ char *ifname;
+ uint32_t ipaddr;
+ } default_route;
+};
+
+struct osysmon_state_gbproxy {
+ char ns_state[32];
+ char bssgp_state[32];
+};
+
+struct osysmon_state_bts {
+ bool rf_locked;
+};
+
+struct osysmon_state_bsc {
+ struct osysmon_state_bts bts[8];
+ unsigned int num_bts;
+ char a_state[32];
+};
+
+struct osysmon_state {
+ struct osysmon_state_sysinfo sysinfo;
+ struct osysmon_state_rtnl rtnl;
+ struct osysmon_state_rtnl gbproxy;
+ struct osysmon_state_rtnl bsc;
+};
diff --git a/simple_ctrl.c b/simple_ctrl.c
new file mode 100644
index 0000000..6b1c8a8
--- /dev/null
+++ b/simple_ctrl.c
@@ -0,0 +1,227 @@
+/* Simple, blocking client API against the Osmocom CTRL interface */
+
+/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 <unistd.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/ipa.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+
+#include "simple_ctrl.h"
+
+struct simple_ctrl_handle {
+ int fd;
+ uint32_t next_id;
+};
+
+struct simple_ctrl_handle *simple_ctrl_open(void *ctx, const char *host, uint16_t dport)
+{
+ struct simple_ctrl_handle *sch;
+ int rc;
+
+ rc = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP, host, dport, OSMO_SOCK_F_CONNECT);
+ if (rc < 0) {
+ fprintf(stderr, "CTRL: error connecting socket: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ sch = talloc_zero(ctx, struct simple_ctrl_handle);
+ if (!sch) {
+ close(rc);
+ return NULL;
+ }
+ sch->fd = rc;
+ return sch;
+}
+
+void simple_ctrl_close(struct simple_ctrl_handle *sch)
+{
+ close(sch->fd);
+ talloc_free(sch);
+}
+
+static struct msgb *simple_ipa_receive(struct simple_ctrl_handle *sch)
+{
+ struct ipaccess_head hh;
+ struct msgb *resp;
+ int rc, len;
+
+ rc = read(sch->fd, (uint8_t *) &hh, sizeof(hh));
+ if (rc != sizeof(hh)) {
+ fprintf(stderr, "CTRL: ERROR: short read (header)\n");
+ return NULL;
+ }
+ len = ntohs(hh.len);
+
+ resp = msgb_alloc(len+sizeof(hh), "CTRL Rx");
+ if (!resp)
+ return NULL;
+ resp->l1h = msgb_put(resp, sizeof(hh));
+ memcpy(resp->l1h, (uint8_t *) &hh, sizeof(hh));
+
+ resp->l2h = resp->tail;
+ rc = read(sch->fd, resp->l2h, len);
+ if (rc < len) {
+ fprintf(stderr, "CTRL: ERROR: short read (payload)\n");
+ msgb_free(resp);
+ return NULL;
+ }
+ msgb_put(resp, rc);
+
+ return resp;
+}
+
+static struct msgb *simple_ctrl_receive(struct simple_ctrl_handle *sch)
+{
+ struct msgb *resp;
+ struct ipaccess_head *ih;
+ struct ipaccess_head_ext *ihe;
+
+ /* loop until we've received a CTRL message */
+ while (true) {
+ resp = simple_ipa_receive(sch);
+ if (!resp)
+ return NULL;
+
+ ih = (struct ipaccess_head *) resp->l1h;
+ if (ih->proto == IPAC_PROTO_OSMO)
+ resp->l2h = resp->l2h+1;
+ ihe = (struct ipaccess_head_ext*) (resp->l1h + sizeof(*ih));
+ if (ih->proto == IPAC_PROTO_OSMO && ihe->proto == IPAC_PROTO_EXT_CTRL)
+ return resp;
+ else {
+ fprintf(stderr, "unknown IPA message %s\n", msgb_hexdump(resp));
+ msgb_free(resp);
+ }
+ }
+}
+
+static int simple_ctrl_send(struct simple_ctrl_handle *sch, struct msgb *msg)
+{
+ int rc;
+
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+
+ rc = write(sch->fd, msg->data, msg->len);
+
+ if (rc < msg->len) {
+ fprintf(stderr, "CTRL: ERROR: short write\n");
+ msgb_free(msg);
+ return rc;
+ } else {
+ msgb_free(msg);
+ return 0;
+ }
+}
+
+static struct msgb *simple_ctrl_xceive(struct simple_ctrl_handle *sch, struct msgb *msg)
+{
+ int rc;
+
+ rc = simple_ctrl_send(sch, msg);
+ if (rc < 0)
+ return NULL;
+
+ /* FIXME: ignore any TRAP */
+ /* FIXME: check string is zero-terminated */
+ return simple_ctrl_receive(sch);
+}
+
+char *simple_ctrl_get(struct simple_ctrl_handle *sch, const char *var)
+{
+ struct msgb *msg = msgb_alloc_headroom(512+8, 8, "CTRL GET");
+ struct msgb *resp;
+ unsigned int rx_id;
+ char *rx_var, *rx_val;
+ int rc;
+
+ if (!msg)
+ return NULL;
+
+ rc = msgb_printf(msg, "GET %u %s", sch->next_id++, var);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ resp = simple_ctrl_xceive(sch, msg);
+
+ rc = sscanf(msgb_l2(resp), "GET_REPLY %u %ms %ms", &rx_id, &rx_var, &rx_val);
+ if (rc == 3) {
+ if (rx_id == sch->next_id-1 && !strcmp(var, rx_var)) {
+ free(rx_var);
+ msgb_free(resp);
+ return rx_val;
+ }
+ free(rx_var);
+ free(rx_val);
+ } else {
+ fprintf(stderr, "CTRL: ERROR: GET(%s) results in '%s'\n", var, (char *)msgb_l2(resp));
+ }
+
+ msgb_free(resp);
+ return NULL;
+}
+
+int simple_ctrl_set(struct simple_ctrl_handle *sch, const char *var, const char *val)
+{
+ struct msgb *msg = msgb_alloc_headroom(512+8, 8, "CTRL SET");
+ struct msgb *resp;
+ unsigned int rx_id;
+ char *rx_var, *rx_val;
+ int rc;
+
+ if (!msg)
+ return -1;
+
+ rc = msgb_printf(msg, "SET %u %s %s", sch->next_id++, var, val);
+ if (rc < 0) {
+ msgb_free(msg);
+ return -1;
+ }
+ resp = simple_ctrl_xceive(sch, msg);
+
+ if (sscanf(msgb_l2(resp), "SET_REPLY %u %ms %ms", &rx_id, &rx_var, &rx_val) == 3) {
+ if (rx_id == sch->next_id-1 && !strcmp(var, rx_var) && !strcmp(val, rx_val)) {
+ free(rx_val);
+ free(rx_var);
+ msgb_free(resp);
+ return 0;
+ } else {
+ free(rx_val);
+ free(rx_var);
+ }
+ } else {
+ fprintf(stderr, "CTRL: ERROR: SET(%s=%s) results in '%s'\n", var, val, (char *) msgb_l2(resp));
+ }
+
+ msgb_free(resp);
+ return -1;
+}
diff --git a/simple_ctrl.h b/simple_ctrl.h
new file mode 100644
index 0000000..e45479f
--- /dev/null
+++ b/simple_ctrl.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdint.h>
+
+struct simple_ctrl_handle;
+
+struct simple_ctrl_handle *simple_ctrl_open(void *ctx, const char *host, uint16_t dport);
+void simple_ctrl_close(struct simple_ctrl_handle *sch);
+
+char *simple_ctrl_get(struct simple_ctrl_handle *sch, const char *var);
+int simple_ctrl_set(struct simple_ctrl_handle *sch, const char *var, const char *val);
+