FreeCalypso > hg > osmo-playpen
changeset 20:2230a763713f
ctrl-client: import from osmo-sysmon git repo
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 17 Dec 2023 08:43:13 +0000 |
parents | 9ff041d85da5 |
children | 80849380395d |
files | ctrl-client/osmo-ctrl-client.c ctrl-client/simple_ctrl.c ctrl-client/simple_ctrl.h |
diffstat | 3 files changed, 439 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ctrl-client/osmo-ctrl-client.c Sun Dec 17 08:43:13 2023 +0000 @@ -0,0 +1,86 @@ +/* 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "simple_ctrl.h" + +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/application.h> + +static struct log_info log_info = {}; + +static void exit_help(void) +{ + printf("Usage:\n"); + printf("\tosmo-ctrl-client HOST PORT get VARIABLE\n"); + printf("\tosmo-ctrl-client HOST PORT set VARIABLE VALUE\n"); + printf("\tosmo-ctrl-client HOST PORT monitor\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, 1000); + 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); + } else if (!strcmp(argv[3], "monitor")) { + simple_ctrl_set_timeout(sch, 0); + while (true) { + struct msgb *msg = simple_ctrl_receive(sch); + if (!msg) + exit(1); + printf("%s", (char *) msgb_l2(msg)); + msgb_free(msg); + } + } else + exit_help(); + + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ctrl-client/simple_ctrl.c Sun Dec 17 08:43:13 2023 +0000 @@ -0,0 +1,337 @@ +/* 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. + */ + +#include <unistd.h> +#include <stdint.h> +#include <talloc.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.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 "client.h" +#include "simple_ctrl.h" + +#define CTRL_ERR(sch, fmt, args...) \ + fprintf(stderr, "CTRL %s error: " fmt, make_authority(sch, &sch->cfg), ##args) + +/*********************************************************************** + * blocking I/O with timeout helpers + ***********************************************************************/ + +static struct timeval *timeval_from_msec(uint32_t tout_msec) +{ + static struct timeval tout; + + if (tout_msec == 0) + return NULL; + tout.tv_sec = tout_msec/1000; + tout.tv_usec = (tout_msec%1000)*1000; + + return &tout; +} + +static ssize_t read_timeout(int fd, void *buf, size_t count, uint32_t tout_msec) +{ + fd_set readset; + int rc; + + FD_ZERO(&readset); + FD_SET(fd, &readset); + + rc = select(fd+1, &readset, NULL, NULL, timeval_from_msec(tout_msec)); + if (rc < 0) + return rc; + + if (FD_ISSET(fd, &readset)) + return read(fd, buf, count); + + return -ETIMEDOUT; +} + +static ssize_t write_timeout(int fd, const void *buf, size_t count, uint32_t tout_msec) +{ + fd_set writeset; + int rc; + + FD_ZERO(&writeset); + FD_SET(fd, &writeset); + + rc = select(fd+1, NULL, &writeset, NULL, timeval_from_msec(tout_msec)); + if (rc < 0) + return rc; + + if (FD_ISSET(fd, &writeset)) + return write(fd, buf, count); + + return -ETIMEDOUT; +} + + +/*********************************************************************** + * actual CTRL client API + ***********************************************************************/ + +struct simple_ctrl_handle { + int fd; + uint32_t next_id; + uint32_t tout_msec; + struct host_cfg cfg; +}; + +struct simple_ctrl_handle *simple_ctrl_open(void *ctx, const char *host, uint16_t dport, + uint32_t tout_msec) +{ + struct simple_ctrl_handle *sch = talloc_zero(ctx, struct simple_ctrl_handle); + fd_set writeset; + int off = 0; + int rc, fd; + + if (!sch) + return NULL; + + sch->cfg.name = talloc_strdup(sch, "simple-ctrl"); + sch->cfg.remote_host = talloc_strdup(sch, host); + sch->cfg.remote_port = dport; + + fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP, host, dport, + OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_NONBLOCK); + if (fd < 0) { + CTRL_ERR(sch, "connecting socket: %s\n", strerror(errno)); + return NULL; + } + + /* wait until connect (or timeout) happens */ + FD_ZERO(&writeset); + FD_SET(fd, &writeset); + rc = select(fd+1, NULL, &writeset, NULL, timeval_from_msec(tout_msec)); + if (rc == 0) { + CTRL_ERR(sch, "timeout during connect\n"); + goto out_close; + } + if (rc < 0) { + CTRL_ERR(sch, "error connecting socket: %s\n", strerror(errno)); + goto out_close; + } + + /* set FD blocking again */ + if (ioctl(fd, FIONBIO, (unsigned char *)&off) < 0) { + CTRL_ERR(sch, "cannot set socket blocking: %s\n", strerror(errno)); + goto out_close; + } + + sch->fd = fd; + sch->tout_msec = tout_msec; + return sch; + +out_close: + close(fd); + return NULL; +} + +void simple_ctrl_set_timeout(struct simple_ctrl_handle *sch, uint32_t tout_msec) +{ + sch->tout_msec = tout_msec; +} + +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_timeout(sch->fd, (uint8_t *) &hh, sizeof(hh), sch->tout_msec); + if (rc < 0) { + CTRL_ERR(sch, "read(): %d\n", rc); + return NULL; + } else if (rc < sizeof(hh)) { + CTRL_ERR(sch, "short read (header)\n"); + return NULL; + } + len = ntohs(hh.len); + + resp = msgb_alloc(len+sizeof(hh)+1, "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) { + CTRL_ERR(sch, "short read (payload)\n"); + msgb_free(resp); + return NULL; + } + msgb_put(resp, rc); + + return resp; +} + +struct msgb *simple_ctrl_receive(struct simple_ctrl_handle *sch) +{ + struct msgb *resp; + struct ipaccess_head *ih; + struct ipaccess_head_ext *ihe; + unsigned char *tmp; + + /* 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) { + /* Ensure data is NULL terminated */ + tmp = msgb_put(resp, 1); + *tmp = '\0'; + return resp; + } else { + CTRL_ERR(sch, "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_timeout(sch->fd, msg->data, msg->len, sch->tout_msec); + if (rc < 0) { + CTRL_ERR(sch, "write(): %d\n", rc); + return rc; + } else if (rc < msg->len) { + CTRL_ERR(sch, "short write\n"); + msgb_free(msg); + return -1; + } 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 */ + 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); + if (!resp) + return NULL; + + rc = sscanf(msgb_l2(resp), "GET_REPLY %u %ms %ms", &rx_id, &rx_var, &rx_val); + if ((rc == 2) || (rc == 3)) { + /* If body is empty return an empty string */ + if (rc == 2) + rx_val = strdup(""); + + 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 { + CTRL_ERR(sch, "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 (!resp) + return -1; + + 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 { + CTRL_ERR(sch, "SET(%s=%s) results in '%s'\n", var, val, (char *) msgb_l2(resp)); + } + + msgb_free(resp); + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ctrl-client/simple_ctrl.h Sun Dec 17 08:43:13 2023 +0000 @@ -0,0 +1,16 @@ +#pragma once + +#include <stdint.h> + +struct simple_ctrl_handle; + +struct simple_ctrl_handle *simple_ctrl_open(void *ctx, const char *host, uint16_t dport, + uint32_t tout_msec); +void simple_ctrl_close(struct simple_ctrl_handle *sch); + +void simple_ctrl_set_timeout(struct simple_ctrl_handle *sch, uint32_t tout_msec); +struct msgb *simple_ctrl_receive(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); +