client: major restructuring around new main_fsm

The remsim_client code already used FSMs for the connections
to both remsim-server and remsim-bankd.  However the 'main' part of the
program was not yet implemented as a FSM, making it somewhat difficult
to perform the right actions in every possible situation.

This commit re-structures the code around a central main_fsm, which
gets notified from the per-connection FSMs and which handles the common
processing.  It also handles the execution of external script commands,
and hence further unifies the code base between the different backends
(simtrace2, ifd_handler, shell)

Closes: #4414

Change-Id: I44a430bc5674dea00ed72a0b28729ac8bcb4e022
diff --git a/src/client/remsim_client.c b/src/client/remsim_client.c
index dd78c8e..182894f 100644
--- a/src/client/remsim_client.c
+++ b/src/client/remsim_client.c
@@ -20,6 +20,10 @@
  *
  */
 
+/* This file contains code shared among all remsim client applications,
+ * including the ifd-handler, which is not an executable program with a main()
+ * function or command line parsing, but a shared library */
+
 #include <errno.h>
 #include <string.h>
 
@@ -33,8 +37,38 @@
 #include "client.h"
 #include "debug.h"
 
+struct client_config *client_config_init(void *ctx)
+{
+	struct client_config *cfg = talloc_zero(ctx, struct client_config);
+	if (!cfg)
+		return NULL;
+
+	cfg->server_host = talloc_strdup(cfg, "127.0.0.1");
+	cfg->server_port = 9998;
+	cfg->client_id = -1;
+	cfg->client_slot = -1;
+	cfg->gsmtap_host = talloc_strdup(cfg, "127.0.0.1");
+	cfg->keep_running = false;
+
+	cfg->usb.vendor_id = -1;
+	cfg->usb.product_id = -1;
+	cfg->usb.config_id = -1;
+	cfg->usb.if_num = -1;
+	cfg->usb.altsetting = 0;
+	cfg->usb.addr = -1;
+	cfg->usb.path = NULL;
+
+	cfg->atr.data[0] = 0x3B;
+	cfg->atr.data[1] = 0x00; // the shortest simplest ATR possible
+	cfg->atr.len = 2;
+
+	return cfg;
+};
+
 static int bankd_handle_rx(struct rspro_server_conn *bankdc, const RsproPDU_t *pdu)
 {
+	struct bankd_client *bc = bankdc2bankd_client(bankdc);
+
 	switch (pdu->msg.present) {
 	case RsproPDUchoice_PR_connectClientRes:
 		/* Store 'identity' of bankd to in peer_comp_id */
@@ -42,8 +76,11 @@
 		osmo_fsm_inst_dispatch(bankdc->fi, SRVC_E_CLIENT_CONN_RES, (void *) pdu);
 		break;
 	case RsproPDUchoice_PR_tpduCardToModem:
+		return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_TPDU, (void *) pdu);
 	case RsproPDUchoice_PR_setAtrReq:
-		return client_user_bankd_handle_rx(bankdc, pdu);
+		return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_ATR, (void *) pdu);
+	case RsproPDUchoice_PR_bankSlotStatusInd:
+		return osmo_fsm_inst_dispatch(bc->main_fi, MF_E_BANKD_SLOT_STATUS, (void *) pdu);
 	default:
 		LOGPFSML(bankdc->fi, LOGL_ERROR, "Unknown/Unsupported RSPRO PDU %s\n",
 			 rspro_msgt_name(pdu));
@@ -78,19 +115,7 @@
 		server_conn_send_rspro(srvc, resp);
 		break;
 	case RsproPDUchoice_PR_configClientBankReq:
-		/* store/set the bankd ip/port as instructed by the server */
-		osmo_talloc_replace_string(bc, &bc->bankd_conn.server_host,
-					   rspro_IpAddr2str(&pdu->msg.choice.configClientBankReq.bankd.ip));
-		rspro2bank_slot(&bc->bankd_slot, &pdu->msg.choice.configClientBankReq.bankSlot);
-		bc->bankd_conn.server_port = pdu->msg.choice.configClientBankReq.bankd.port;
-		/* bankd port 0 is a magic value to indicate "no bankd" */
-		if (bc->bankd_conn.server_port == 0)
-			osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_DISCONNECT, NULL);
-		else
-			osmo_fsm_inst_dispatch(bc->bankd_conn.fi, SRVC_E_ESTABLISH, NULL);
-		/* send response to server */
-		resp = rspro_gen_ConfigClientBankRes(ResultCode_ok);
-		server_conn_send_rspro(srvc, resp);
+		osmo_fsm_inst_dispatch(bc->main_fi, MF_E_SRVC_CONFIG_BANK, (void *) pdu);
 		break;
 	default:
 		LOGPFSML(srvc->fi, LOGL_ERROR, "Unknown/Unsupported RSPRO PDU type: %s\n",
@@ -101,7 +126,8 @@
 	return 0;
 }
 
-struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software)
+struct bankd_client *remsim_client_create(void *ctx, const char *name, const char *software,
+					  struct client_config *cfg)
 {
 	struct bankd_client *bc = talloc_zero(ctx, struct bankd_client);
 	struct rspro_server_conn *srvc, *bankdc;
@@ -110,10 +136,20 @@
 	if (!bc)
 		return NULL;
 
+	bc->cfg = cfg;
+
+	bc->main_fi = main_fsm_alloc(bc, bc);
+	if (!bc->main_fi) {
+		fprintf(stderr, "Unable to create main client FSM: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	remsim_client_set_clslot(bc, cfg->client_id, cfg->client_slot);
+
 	/* create and [attempt to] establish connection to remsim-server */
 	srvc = &bc->srv_conn;
-	srvc->server_host = "localhost";
-	srvc->server_port = 9998;
+	srvc->server_host = cfg->server_host;
+	srvc->server_port = cfg->server_port;
 	srvc->handle_rx = srvc_handle_rx;
 	srvc->own_comp_id.type = ComponentType_remsimClient;
 	OSMO_STRLCPY_ARRAY(srvc->own_comp_id.name, name);
@@ -125,6 +161,9 @@
 		fprintf(stderr, "Unable to create Server conn FSM: %s\n", strerror(errno));
 		exit(1);
 	}
+	osmo_fsm_inst_change_parent(srvc->fi, bc->main_fi, MF_E_SRVC_LOST);
+	srvc->parent_conn_evt = MF_E_SRVC_CONNECTED;
+	srvc->parent_disc_evt = MF_E_SRVC_LOST;
 
 	bankdc = &bc->bankd_conn;
 	/* server_host / server_port are configured from remsim-server */
@@ -136,6 +175,9 @@
 		exit(1);
 	}
 	osmo_fsm_inst_update_id(bankdc->fi, "bankd");
+	osmo_fsm_inst_change_parent(bankdc->fi, bc->main_fi, MF_E_BANKD_LOST);
+	bankdc->parent_conn_evt = MF_E_BANKD_CONNECTED;
+	bankdc->parent_disc_evt = MF_E_BANKD_LOST;
 
 	return bc;
 }