Keep sgsn subsystems under struct sgsn_instance lifecycle

Rework initialization and destruction of several sgsn subsystems to be
allocated & released together with the struct sgsn_instance.

This makes it easier to destroy and recreate the entire context and
allows us to start moving global variables scattered around to be under
struct sgsn_instance.

Change-Id: Idf60519b8e475b94d38bbb69e737132a5afaefab
diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h
index 8b9a00e..4f8bc3d 100644
--- a/include/osmocom/sgsn/gprs_sgsn.h
+++ b/include/osmocom/sgsn/gprs_sgsn.h
@@ -381,8 +381,6 @@
 extern struct llist_head sgsn_pdp_ctxts;
 
 uint32_t sgsn_alloc_ptmsi(void);
-struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx);
-void sgsn_inst_init(struct sgsn_instance *sgsn);
 
 char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len, bool return_ipv6);
 
@@ -438,6 +436,4 @@
 /* Called on subscriber data updates */
 void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx);
 
-void sgsn_rate_ctr_init(void);
-
 #endif /* _GPRS_SGSN_H */
diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h
index f08037a..d558d87 100644
--- a/include/osmocom/sgsn/sgsn.h
+++ b/include/osmocom/sgsn/sgsn.h
@@ -152,6 +152,8 @@
 	struct rate_ctr_group *rate_ctrs;
 
 	struct llist_head mme_list; /* list of struct sgsn_mme_ctx */
+
+	struct ctrl_handle *ctrlh;
 };
 
 extern struct sgsn_instance *sgsn;
@@ -163,6 +165,8 @@
 char *sgsn_gtp_ntoa(struct ul16_t *ul);
 
 /* sgsn.c */
+struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx);
+int sgsn_inst_init(struct sgsn_instance *sgsn);
 
 /* Main input function for Gb proxy */
 int sgsn_rcvmsg(struct msgb *msg, struct gprs_ns2_vc *nsvc, uint16_t ns_bvci);
@@ -197,6 +201,7 @@
  * CDR related functionality
  */
 int sgsn_cdr_init(struct sgsn_instance *sgsn);
+void sgsn_cdr_release(struct sgsn_instance *sgsn);
 
 
 /*
diff --git a/src/sgsn/gprs_sgsn.c b/src/sgsn/gprs_sgsn.c
index 572e90b..5afddb4 100644
--- a/src/sgsn/gprs_sgsn.c
+++ b/src/sgsn/gprs_sgsn.c
@@ -27,6 +27,8 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 #include <osmocom/core/backtrace.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/ports.h>
 #include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/gprs/gprs_bssgp.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
@@ -59,6 +61,7 @@
 
 extern struct sgsn_instance *sgsn;
 extern void *tall_sgsn_ctx;
+extern struct osmo_tdef sgsn_T_defs[];
 
 LLIST_HEAD(sgsn_mm_ctxts);
 LLIST_HEAD(sgsn_ggsn_ctxts);
@@ -145,12 +148,6 @@
 	sgsn_ctr_description,
 };
 
-void sgsn_rate_ctr_init(void)
-{
-	sgsn->rate_ctrs = rate_ctr_group_alloc(tall_sgsn_ctx, &sgsn_ctrg_desc, 0);
-	OSMO_ASSERT(sgsn->rate_ctrs);
-}
-
 /* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/
 struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
 {
@@ -893,21 +890,67 @@
 	osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
 }
 
+static int sgsn_instance_talloc_destructor(struct sgsn_instance *sgi)
+{
+	sgsn_cdr_release(sgi);
+	osmo_timer_del(&sgi->llme_timer);
+	rate_ctr_group_free(sgi->rate_ctrs);
+	return 0;
+}
+
 struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx)
 {
 	struct sgsn_instance *inst;
 	inst = talloc_zero(talloc_ctx, struct sgsn_instance);
+
+	talloc_set_destructor(inst, sgsn_instance_talloc_destructor);
+
 	inst->cfg.gtp_statedir = talloc_strdup(inst, "./");
 	inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED;
 	inst->cfg.require_authentication = true; /* only applies if auth_policy is REMOTE */
 	inst->cfg.gsup_server_port = OSMO_GSUP_PORT;
 
+	inst->cfg.T_defs = sgsn_T_defs;
+	osmo_tdefs_reset(inst->cfg.T_defs);
+	inst->cfg.T_defs_gtp = gtp_T_defs;
+	osmo_tdefs_reset(inst->cfg.T_defs_gtp);
+
+	inst->rate_ctrs = rate_ctr_group_alloc(inst, &sgsn_ctrg_desc, 0);
+	OSMO_ASSERT(inst->rate_ctrs);
+
 	INIT_LLIST_HEAD(&inst->mme_list);
+
+	osmo_timer_setup(&inst->llme_timer, sgsn_llme_check_cb, NULL);
+	osmo_timer_schedule(&inst->llme_timer, GPRS_LLME_CHECK_TICK, 0);
+	/* These are mostly setting up stuff not related to VTY cfg, so they can be set up here: */
+	sgsn_auth_init(inst);
+	sgsn_cdr_init(inst);
 	return inst;
 }
 
-void sgsn_inst_init(struct sgsn_instance *sgsn)
+/* To be called after VTY config parsing: */
+int sgsn_inst_init(struct sgsn_instance *sgsn)
 {
-	osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL);
-	osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
+	int rc;
+
+	/* start control interface after reading config for
+	 * ctrl_vty_get_bind_addr() */
+	sgsn->ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_SGSN, NULL);
+	if (!sgsn->ctrlh) {
+		LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n");
+		return -EIO;
+	}
+
+	rc = sgsn_ctrl_cmds_install();
+	if (rc != 0) {
+		LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n");
+		return -EFAULT;
+	}
+
+	rc = gprs_subscr_init(sgsn);
+	if (rc < 0) {
+		LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n");
+		return rc;
+	}
+	return 0;
 }
diff --git a/src/sgsn/sgsn_cdr.c b/src/sgsn/sgsn_cdr.c
index 24d7524..ac0a6fd 100644
--- a/src/sgsn/sgsn_cdr.c
+++ b/src/sgsn/sgsn_cdr.c
@@ -41,7 +41,6 @@
 
 /* TODO...avoid going through a global */
 extern struct sgsn_instance *sgsn;
-extern struct ctrl_handle *g_ctrlh;
 
 /**
  * The CDR module will generate an entry like:
@@ -65,7 +64,7 @@
 
 static void send_cdr_trap(char *value)
 {
-	if (ctrl_cmd_send_trap(g_ctrlh, "cdr-v1", value) < 0)
+	if (ctrl_cmd_send_trap(sgsn->ctrlh, "cdr-v1", value) < 0)
 		LOGP(DGPRS, LOGL_ERROR, "Failed to create and send TRAP cdr-v1\n");
 }
 
@@ -300,3 +299,8 @@
 
 	return 0;
 }
+
+void sgsn_cdr_release(struct sgsn_instance *sgsn)
+{
+	osmo_signal_unregister_handler(SS_SGSN, handle_sgsn_sig, sgsn);
+}
diff --git a/src/sgsn/sgsn_main.c b/src/sgsn/sgsn_main.c
index 3ad0881..7253348 100644
--- a/src/sgsn/sgsn_main.c
+++ b/src/sgsn/sgsn_main.c
@@ -67,9 +67,6 @@
 #include <osmocom/sgsn/gprs_subscriber.h>
 #include <osmocom/sgsn/gtp.h>
 
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-
 #include <gtp.h>
 #include <osmocom/sgsn/sgsn_rim.h>
 
@@ -85,7 +82,6 @@
 #include <getopt.h>
 
 void *tall_sgsn_ctx;
-struct ctrl_handle *g_ctrlh;
 
 struct gprs_ns2_inst *sgsn_nsi;
 static int daemonize = 0;
@@ -418,16 +414,11 @@
 	bssgp_set_bssgp_callback(sgsn_bssgp_dispatch_ns_unitdata_req_cb, sgsn_nsi);
 
 	gprs_llc_init("/usr/local/lib/osmocom/crypt/");
-	sgsn_rate_ctr_init();
-	sgsn_inst_init(sgsn);
-
 
 	gprs_ns2_vty_init(sgsn_nsi);
 	bssgp_vty_init();
 	gprs_llc_vty_init();
 	gprs_sndcp_vty_init();
-	sgsn_auth_init(sgsn);
-	sgsn_cdr_init(sgsn);
 
 	handle_options(argc, argv);
 
@@ -458,20 +449,6 @@
 	if (rc < 0)
 		exit(1);
 
-	/* start control interface after reading config for
-	 * ctrl_vty_get_bind_addr() */
-	g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_SGSN, NULL);
-	if (!g_ctrlh) {
-		LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n");
-		exit(1);
-	}
-
-	if (sgsn_ctrl_cmds_install() != 0) {
-		LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n");
-		exit(1);
-	}
-
-
 	rc = sgsn_gtp_init(sgsn);
 	if (rc) {
 		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n");
@@ -479,9 +456,9 @@
 	} else
 		LOGP(DGPRS, LOGL_NOTICE, "libGTP v%s initialized\n", gtp_version());
 
-	rc = gprs_subscr_init(sgsn);
+	rc = sgsn_inst_init(sgsn);
 	if (rc < 0) {
-		LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n");
+		LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n");
 		exit(2);
 	}
 
diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c
index 18951f8..4affa9f 100644
--- a/src/sgsn/sgsn_vty.c
+++ b/src/sgsn/sgsn_vty.c
@@ -99,7 +99,7 @@
 #define NONSPEC_X1001_SECS     5       /* wait for a RANAP Release Complete */
 
 
-static struct osmo_tdef sgsn_T_defs[] = {
+struct osmo_tdef sgsn_T_defs[] = {
 	{ .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update timer (s)" },
 	{ .T=3313, .default_val=GSM0408_T3313_SECS, .desc="Waiting for paging response timer (s)" },
 	{ .T=3314, .default_val=GSM0408_T3314_SECS, .desc="READY timer. Force to STANDBY on expiry timer (s)" },
@@ -1739,12 +1739,6 @@
 {
 	g_cfg = cfg;
 
-	g_cfg->T_defs = sgsn_T_defs;
-	osmo_tdefs_reset(g_cfg->T_defs);
-
-	g_cfg->T_defs_gtp = gtp_T_defs;
-	osmo_tdefs_reset(g_cfg->T_defs_gtp);
-
 	install_element_ve(&show_sgsn_cmd);
 	//install_element_ve(&show_mmctx_tlli_cmd);
 	install_element_ve(&show_mmctx_imsi_cmd);
diff --git a/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am
index e6cb71b..8deec24 100644
--- a/tests/sgsn/Makefile.am
+++ b/tests/sgsn/Makefile.am
@@ -7,6 +7,7 @@
 	-Wall \
 	-ggdb3 \
 	$(LIBOSMOCORE_CFLAGS) \
+	$(LIBOSMOCTRL_CFLAGS) \
 	$(LIBOSMOABIS_CFLAGS) \
 	$(LIBOSMOGSM_CFLAGS) \
 	$(LIBOSMOGSUPCLIENT_CFLAGS) \
@@ -58,6 +59,8 @@
 	$(top_builddir)/src/sgsn/gprs_sgsn.o \
 	$(top_builddir)/src/sgsn/gtp_ggsn.o \
 	$(top_builddir)/src/sgsn/gtp_mme.o \
+	$(top_builddir)/src/sgsn/sgsn_cdr.o \
+	$(top_builddir)/src/sgsn/sgsn_ctrl.o \
 	$(top_builddir)/src/sgsn/sgsn_vty.o \
 	$(top_builddir)/src/sgsn/sgsn_libgtp.o \
 	$(top_builddir)/src/sgsn/sgsn_auth.o \
@@ -77,6 +80,7 @@
 	$(top_builddir)/src/gprs/sgsn_ares.o \
 	$(LIBOSMOABIS_LIBS) \
 	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOCTRL_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
 	$(LIBOSMOGB_LIBS) \
 	$(LIBOSMOGSUPCLIENT_LIBS) \
diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c
index 18d3c19..7d3bde5 100644
--- a/tests/sgsn/sgsn_test.c
+++ b/tests/sgsn/sgsn_test.c
@@ -45,15 +45,7 @@
 #include "gprs_gb_parse.h"
 
 void *tall_sgsn_ctx;
-static struct sgsn_instance sgsn_inst = {
-	.config_file = "osmo_sgsn.cfg",
-	.cfg = {
-		.gtp_statedir = "./",
-		.auth_policy = SGSN_AUTH_POLICY_CLOSED,
-		.gea_encryption_mask = 0x1,
-	},
-};
-struct sgsn_instance *sgsn = &sgsn_inst;
+struct sgsn_instance *sgsn;
 unsigned sgsn_tx_counter = 0;
 struct msgb *last_msg = NULL;
 struct gprs_gb_parse_context last_dl_parse_ctx;
@@ -70,6 +62,7 @@
 static void cleanup_test(void)
 {
 	reset_last_msg();
+	TALLOC_FREE(sgsn);
 }
 
 static uint32_t get_new_ptmsi(const struct gprs_gb_parse_context *parse_ctx)
@@ -574,6 +567,7 @@
 	printf("Testing subscriber GSUP handling\n");
 
 	update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data;
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* Check for emptiness */
 	OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
@@ -750,6 +744,7 @@
 	uint32_t local_tlli;
 
 	printf("Testing GMM detach\n");
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* DTAP - Detach Request (MO) */
 	/* normal detach, power_off = 0 */
@@ -791,6 +786,7 @@
 	uint32_t local_tlli;
 
 	printf("Testing GMM detach (power off)\n");
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* DTAP - Detach Request (MO) */
 	/* normal detach, power_off = 1 */
@@ -831,6 +827,7 @@
 	uint32_t local_tlli;
 
 	printf("Testing GMM detach (no MMCTX)\n");
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* DTAP - Detach Request (MO) */
 	/* normal detach, power_off = 0 */
@@ -1197,6 +1194,7 @@
 	};
 
 	printf("Testing GMM reject\n");
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* reset the PRNG used by sgsn_alloc_ptmsi */
 	srand(1);
@@ -1243,6 +1241,9 @@
 	uint32_t foreign_tlli;
 	uint32_t local_tlli = 0;
 	struct gprs_llc_lle *lle;
+
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
+	sgsn->cfg.gea_encryption_mask = 0x1;
 	const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy;
 
 	/* DTAP - Attach Request */
@@ -1273,8 +1274,7 @@
 	};
 
 	printf("Testing cancellation\n");
-
-	sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_OPEN;
+	sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN;
 
 	foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN);
 
@@ -1456,6 +1456,7 @@
 	printf("Testing GGSN selection\n");
 
 	osmo_gsup_client_send_cb = my_gsup_client_send_dummy;
+	sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
 
 	/* Check for emptiness */
 	OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
@@ -1654,11 +1655,7 @@
 	tall_sgsn_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "sgsn");
 	msgb_ctx = msgb_talloc_ctx_init(osmo_sgsn_ctx, 0);
 
-	sgsn_rate_ctr_init();
-	sgsn_auth_init(sgsn);
-	gprs_subscr_init(sgsn);
 	vty_init(&vty_info);
-	sgsn_vty_init(&sgsn->cfg);
 
 	test_llme();
 	test_subscriber();
@@ -1678,7 +1675,7 @@
 
 	talloc_report_full(osmo_sgsn_ctx, stderr);
 	OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
-	OSMO_ASSERT(talloc_total_blocks(tall_sgsn_ctx) == 2);
+	OSMO_ASSERT(talloc_total_blocks(tall_sgsn_ctx) == 1);
 	return 0;
 }