sms: Give smsc its own VTY config node

The pre-historic sms_queue code used to have very strange aspects,
such as having some parameters (max-failure, max-pending) which could
only be sent from the 'enable' node, but not from a config file.

Before adding more configuration parameters, let's clean this up by
introducing a proper VTY config node for the 'smsc'; move the existing
config commands there and add new ones for max-failure and max-pending.

As the sms_queue data structure is only allocated after the config file
parsing happens, we are introducing a new 'sms_queue_config' data
structure.  This encapsulates the public readable/writable config
parameters.

Change-Id: Ie8e0ab1a9f979337ff06544b9ab3820954d9804a
diff --git a/include/osmocom/msc/sms_queue.h b/include/osmocom/msc/sms_queue.h
index 492ae72..76f885b 100644
--- a/include/osmocom/msc/sms_queue.h
+++ b/include/osmocom/msc/sms_queue.h
@@ -21,8 +21,6 @@
 
 /* vty helper functions */
 int sms_queue_stats(struct gsm_sms_queue *, struct vty* vty);
-int sms_queue_set_max_pending(struct gsm_sms_queue *, int max);
-int sms_queue_set_max_failure(struct gsm_sms_queue *, int fail);
 int sms_queue_clear(struct gsm_sms_queue *);
 int sms_queue_sms_is_pending(struct gsm_sms_queue *smsq, unsigned long long sms_id);
 
diff --git a/include/osmocom/msc/vty.h b/include/osmocom/msc/vty.h
index 2a3b18b..1e13846 100644
--- a/include/osmocom/msc/vty.h
+++ b/include/osmocom/msc/vty.h
@@ -24,11 +24,13 @@
 	SMPP_ESME_NODE,
 	HLR_NODE,
 	CFG_SGS_NODE,
+	SMSC_NODE,
 };
 
 int bsc_vty_init_extra(void);
 
 void msc_vty_init(struct gsm_network *msc_network);
+void smsc_vty_init(struct gsm_network *msc_network);
 
 struct gsm_network *gsmnet_from_vty(struct vty *vty);
 
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
index e6a2dc1..d3b6035 100644
--- a/src/libmsc/Makefile.am
+++ b/src/libmsc/Makefile.am
@@ -67,6 +67,7 @@
 	sdp_msg.c \
 	silent_call.c \
 	sms_queue.c \
+	smsc_vty.c \
 	transaction.c \
 	msc_net_init.c \
 	ctrl_commands.c \
diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c
index 5ceb6c4..be05a95 100644
--- a/src/libmsc/msc_vty.c
+++ b/src/libmsc/msc_vty.c
@@ -410,7 +410,7 @@
 #define MNCC_GUARD_TIMEOUT_STR "Set global guard timer for mncc interface activity\n"
 #define MNCC_GUARD_TIMEOUT_VALUE_STR "guard timer value (sec.)\n"
 
-DEFUN(cfg_sms_database, cfg_sms_database_cmd,
+DEFUN_DEPRECATED(cfg_sms_database, cfg_sms_database_cmd,
 	"sms-database PATH",
 	"Set the path to the MSC-SMS database file\n"
 	"Relative or absolute file system path to the database file (default is '" SMS_DEFAULT_DB_FILE_PATH "')\n")
@@ -754,8 +754,6 @@
 static int config_write_msc(struct vty *vty)
 {
 	vty_out(vty, "msc%s", VTY_NEWLINE);
-	if (gsmnet->sms_queue_cfg->db_file_path && strcmp(gsmnet->sms_queue_cfg->db_file_path, SMS_DEFAULT_DB_FILE_PATH))
-		vty_out(vty, " sms-database %s%s", gsmnet->sms_queue_cfg->db_file_path, VTY_NEWLINE);
 	if (gsmnet->mncc_sock_path)
 		vty_out(vty, " mncc external %s%s", gsmnet->mncc_sock_path, VTY_NEWLINE);
 	vty_out(vty, " mncc guard-timeout %i%s",
@@ -1860,51 +1858,6 @@
 	return CMD_SUCCESS;
 }
 
-DEFUN(show_smsqueue,
-      show_smsqueue_cmd,
-      "show sms-queue",
-      SHOW_STR "Display SMSqueue statistics\n")
-{
-	sms_queue_stats(gsmnet->sms_queue, vty);
-	return CMD_SUCCESS;
-}
-
-DEFUN(smsqueue_trigger,
-      smsqueue_trigger_cmd,
-      "sms-queue trigger",
-      "SMS Queue\n" "Trigger sending messages\n")
-{
-	sms_queue_trigger(gsmnet->sms_queue);
-	return CMD_SUCCESS;
-}
-
-DEFUN(smsqueue_max,
-      smsqueue_max_cmd,
-      "sms-queue max-pending <1-500>",
-      "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n")
-{
-	sms_queue_set_max_pending(gsmnet->sms_queue, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(smsqueue_clear,
-      smsqueue_clear_cmd,
-      "sms-queue clear",
-      "SMS Queue\n" "Clear the queue of pending SMS\n")
-{
-	sms_queue_clear(gsmnet->sms_queue);
-	return CMD_SUCCESS;
-}
-
-DEFUN(smsqueue_fail,
-      smsqueue_fail_cmd,
-      "sms-queue max-failure <1-500>",
-      "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n")
-{
-	sms_queue_set_max_failure(gsmnet->sms_queue, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
 
 DEFUN(cfg_mncc_int, cfg_mncc_int_cmd,
       "mncc-int", "Configure internal MNCC handler")
@@ -2118,6 +2071,7 @@
 	ranap_iu_vty_init(MSC_NODE, (enum ranap_nsap_addr_enc*)&msc_network->iu.rab_assign_addr_enc);
 #endif
 	sgs_vty_init();
+	smsc_vty_init(msc_network);
 
 	osmo_fsm_vty_add_cmds();
 
@@ -2143,14 +2097,9 @@
 	install_element_ve(&subscriber_mstest_open_cmd);
 	install_element_ve(&subscriber_paging_cmd);
 	install_element_ve(&show_stats_cmd);
-	install_element_ve(&show_smsqueue_cmd);
 	install_element_ve(&logging_fltr_imsi_cmd);
 
 	install_element(ENABLE_NODE, &ena_subscr_expire_cmd);
-	install_element(ENABLE_NODE, &smsqueue_trigger_cmd);
-	install_element(ENABLE_NODE, &smsqueue_max_cmd);
-	install_element(ENABLE_NODE, &smsqueue_clear_cmd);
-	install_element(ENABLE_NODE, &smsqueue_fail_cmd);
 	install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd);
 	install_element(ENABLE_NODE, &subscriber_sms_delete_all_cmd);
 
diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c
index e0433ee..a47f8e0 100644
--- a/src/libmsc/sms_queue.c
+++ b/src/libmsc/sms_queue.c
@@ -672,22 +672,6 @@
 	return 0;
 }
 
-int sms_queue_set_max_pending(struct gsm_sms_queue *smsq, int max_pending)
-{
-	LOGP(DLSMS, LOGL_NOTICE, "SMSqueue old max: %d new: %d\n",
-	     smsq->cfg->max_pending, max_pending);
-	smsq->cfg->max_pending = max_pending;
-	return 0;
-}
-
-int sms_queue_set_max_failure(struct gsm_sms_queue *smsq, int max_fail)
-{
-	LOGP(DLSMS, LOGL_NOTICE, "SMSqueue max failure old: %d new: %d\n",
-	     smsq->cfg->max_fail, max_fail);
-	smsq->cfg->max_fail = max_fail;
-	return 0;
-}
-
 int sms_queue_clear(struct gsm_sms_queue *smsq)
 {
 	struct gsm_sms_pending *pending, *tmp;
diff --git a/src/libmsc/smsc_vty.c b/src/libmsc/smsc_vty.c
new file mode 100644
index 0000000..cdb81c6
--- /dev/null
+++ b/src/libmsc/smsc_vty.c
@@ -0,0 +1,168 @@
+/* SMSC interface to VTY */
+/* (C) 2016-2018 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
+ * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
+ * (C) 2009-2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2011 by Holger Hans Peter Freyther
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/msc/vty.h>
+#include <osmocom/msc/gsm_data.h>
+#include <osmocom/msc/sms_queue.h>
+
+
+static struct gsm_network *gsmnet;
+static struct sms_queue_config *smqcfg;
+
+/***********************************************************************
+ * SMSC Config Node
+ ***********************************************************************/
+
+static struct cmd_node smsc_node = {
+	SMSC_NODE,
+	"%s(config-smsc)# ",
+	1,
+};
+
+DEFUN(cfg_smsc, cfg_smsc_cmd,
+      "smsc", "Configure SMSC options")
+{
+	vty->node = SMSC_NODE;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_sms_database, cfg_sms_database_cmd,
+	"database PATH",
+	"Set the path to the MSC-SMS database file\n"
+	"Relative or absolute file system path to the database file (default is '" SMS_DEFAULT_DB_FILE_PATH "')\n")
+{
+	osmo_talloc_replace_string(smqcfg, &smqcfg->db_file_path, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_sms_queue_max, cfg_sms_queue_max_cmd,
+      "queue max-pending <1-500>",
+      "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n")
+{
+	smqcfg->max_pending = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_sms_queue_fail, cfg_sms_queue_fail_cmd,
+      "queue max-failure <1-500>",
+      "SMS Queue\n" "Maximum number of delivery failures before giving up\n" "Amount\n")
+{
+	smqcfg->max_fail = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+/***********************************************************************
+ * View / Enable Node
+ ***********************************************************************/
+
+DEFUN(show_smsqueue,
+      show_smsqueue_cmd,
+      "show sms-queue",
+      SHOW_STR "Display SMSqueue statistics\n")
+{
+	sms_queue_stats(gsmnet->sms_queue, vty);
+	return CMD_SUCCESS;
+}
+
+DEFUN(smsqueue_trigger,
+      smsqueue_trigger_cmd,
+      "sms-queue trigger",
+      "SMS Queue\n" "Trigger sending messages\n")
+{
+	sms_queue_trigger(gsmnet->sms_queue);
+	return CMD_SUCCESS;
+}
+
+DEFUN(smsqueue_max,
+      smsqueue_max_cmd,
+      "sms-queue max-pending <1-500>",
+      "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n")
+{
+	int max_pending = atoi(argv[0]);
+	vty_out(vty, "%% SMSqueue old max: %d new: %d%s",
+		smqcfg->max_pending, max_pending, VTY_NEWLINE);
+	smqcfg->max_pending = max_pending;
+	return CMD_SUCCESS;
+}
+
+DEFUN(smsqueue_clear,
+      smsqueue_clear_cmd,
+      "sms-queue clear",
+      "SMS Queue\n" "Clear the queue of pending SMS\n")
+{
+	sms_queue_clear(gsmnet->sms_queue);
+	return CMD_SUCCESS;
+}
+
+DEFUN(smsqueue_fail,
+      smsqueue_fail_cmd,
+      "sms-queue max-failure <1-500>",
+      "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n")
+{
+	int max_fail = atoi(argv[0]);
+	vty_out(vty, "%% SMSqueue max failure old: %d new: %d%s",
+		smqcfg->max_fail, max_fail, VTY_NEWLINE);
+	smqcfg->max_fail = max_fail;
+	return CMD_SUCCESS;
+}
+
+static int config_write_smsc(struct vty *vty)
+{
+	vty_out(vty, "smsc%s", VTY_NEWLINE);
+
+	if (smqcfg->db_file_path && strcmp(smqcfg->db_file_path, SMS_DEFAULT_DB_FILE_PATH))
+		vty_out(vty, " database %s%s", smqcfg->db_file_path, VTY_NEWLINE);
+
+	vty_out(vty, " queue max-pending %u%s", smqcfg->max_pending, VTY_NEWLINE);
+	vty_out(vty, " queue max-failure %u%s", smqcfg->max_fail, VTY_NEWLINE);
+
+	return 0;
+}
+
+void smsc_vty_init(struct gsm_network *msc_network)
+{
+	OSMO_ASSERT(gsmnet == NULL);
+	gsmnet = msc_network;
+	smqcfg = msc_network->sms_queue_cfg;
+
+	/* config node */
+	install_element(CONFIG_NODE, &cfg_smsc_cmd);
+	install_node(&smsc_node, config_write_smsc);
+	install_element(SMSC_NODE, &cfg_sms_database_cmd);
+	install_element(SMSC_NODE, &cfg_sms_queue_max_cmd);
+	install_element(SMSC_NODE, &cfg_sms_queue_fail_cmd);
+
+	/* enable node */
+	install_element(ENABLE_NODE, &smsqueue_trigger_cmd);
+	install_element(ENABLE_NODE, &smsqueue_max_cmd);
+	install_element(ENABLE_NODE, &smsqueue_clear_cmd);
+	install_element(ENABLE_NODE, &smsqueue_fail_cmd);
+
+	/* view / enable node */
+	install_element_ve(&show_smsqueue_cmd);
+}
diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c
index 9329de5..bdffb41 100644
--- a/src/osmo-msc/msc_main.c
+++ b/src/osmo-msc/msc_main.c
@@ -205,7 +205,7 @@
 			break;
 		case 'l':
 			fprintf(stderr, "Command line argument '-%c' is deprecated, use VTY "
-				"parameter 'msc' / 'sms-database %s' instead.\n", c, optarg);
+				"parameter 'smsc' / 'database %s' instead.\n", c, optarg);
 			exit(2);
 			break;
 		case 'c':
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index 14f15a2..a67f202 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -5,6 +5,7 @@
   network
   msc
   sgs
+  smsc
   mncc-int
   hlr
 ...
@@ -46,7 +47,6 @@
 OsmoMSC(config)# msc
 OsmoMSC(config-msc)# list
 ...
-  sms-database PATH
   assign-tmsi
   lcls-permitted
   no lcls-permitted
@@ -178,6 +178,9 @@
  local-port 29118
  local-ip 0.0.0.0
  vlr-name vlr.example.net
+smsc
+ queue max-pending 20
+ queue max-failure 1
 end
 
 OsmoMSC# configure terminal