bankd: Implement re-opening/connecting to card/reader

Change-Id: I5f4b12a76f82776cfd929bd56a4a1f0d4217be65
diff --git a/src/bankd.h b/src/bankd.h
index 21d0ccb..86a66b9 100644
--- a/src/bankd.h
+++ b/src/bankd.h
@@ -99,6 +99,8 @@
 	unsigned int num;
 	/* worker thread state */
 	enum bankd_worker_state state;
+	/* timeout to use for blocking read */
+	unsigned int timeout;
 
 	/* slot number we are representing */
 	struct bank_slot slot;
diff --git a/src/bankd_main.c b/src/bankd_main.c
index 063b475..f755b8f 100644
--- a/src/bankd_main.c
+++ b/src/bankd_main.c
@@ -198,25 +198,39 @@
 {
 	long rc;
 
-	/* resolve PC/SC reader name from slot_id -> name map */
-	worker->reader.name = bankd_pcsc_get_slot_name(worker->bankd, &worker->slot);
+	if (!worker->reader.name) {
+		/* resolve PC/SC reader name from slot_id -> name map */
+		worker->reader.name = bankd_pcsc_get_slot_name(worker->bankd, &worker->slot);
+		if (!worker->reader.name) {
+			LOGW(worker, "No PC/SC reader name configured for %u/%u, fix your config\n",
+				worker->slot.bank_id, worker->slot.slot_nr);
+			rc = -1;
+			goto end;
+		}
+	}
 	OSMO_ASSERT(worker->reader.name);
 
-	LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
+	if (!worker->reader.pcsc.hContext) {
+		LOGW(worker, "Attempting to open PC/SC context\n");
+		/* The PC/SC context must be created inside the thread where we'll later use it */
+		rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
+		PCSC_ERROR(worker, rc, "SCardEstablishContext")
+	}
 
-	/* The PC/SC context must be created inside the thread where we'll later use it */
-	rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
-	PCSC_ERROR(worker, rc, "SCardEstablishContext")
-
-	DWORD dwActiveProtocol;
-	rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
-			  SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
-	PCSC_ERROR(worker, rc, "SCardConnect")
+	if (!worker->reader.pcsc.hCard) {
+		LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
+		DWORD dwActiveProtocol;
+		rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
+				  SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
+		PCSC_ERROR(worker, rc, "SCardConnect")
+	}
 
 	worker_set_state(worker, BW_ST_CONN_CLIENT_MAPPED_CARD);
 
 	return 0;
 end:
+	/* retry in 10s */
+	worker->timeout = 10;
 	return rc;
 }
 
@@ -386,6 +400,16 @@
 	return rc; 
 }
 
+static int wait_for_fd_or_timeout(int fd, unsigned int timeout_secs)
+{
+	struct timeval tout = { timeout_secs, 0 };
+	fd_set readset;
+
+	FD_ZERO(&readset);
+	FD_SET(fd, &readset);
+	return select(fd + 1, &readset, NULL, NULL, timeout_secs ? &tout : NULL);
+}
+
 /* body of the main transceive loop */
 static int worker_transceive_loop(struct bankd_worker *worker)
 {
@@ -396,6 +420,13 @@
 	int data_len, rc;
 	RsproPDU_t *pdu = NULL;
 
+	rc = wait_for_fd_or_timeout(worker->client.fd, worker->timeout);
+	if (rc == 0) {
+		/* TIMEOUT case */
+		worker_open_card(worker);
+		return 0;
+	};
+
 	/* 1) blocking read of entire IPA message from the socket */
 	rc = blocking_ipa_read(worker->client.fd, buf, sizeof(buf));
 	if (rc < 0)
@@ -514,6 +545,8 @@
 			SCardReleaseContext(worker->reader.pcsc.hContext);
 			worker->reader.pcsc.hContext = 0;
 		}
+		if (worker->reader.name)
+			worker->reader.name = NULL;
 		if (worker->client.fd >= 0)
 			close(worker->client.fd);
 		memset(&worker->client.peer_addr, 0, sizeof(worker->client.peer_addr));