Change in pysim[master]: pySim-shell: allow card insertion at runtime

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

dexter gerrit-no-reply at lists.osmocom.org
Thu Sep 23 09:38:09 UTC 2021


dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/25549 )


Change subject: pySim-shell: allow card insertion at runtime
......................................................................

pySim-shell: allow card insertion at runtime

Currently a card must be present in the reader until the user can enter
pySim-shell. Removing and plugging another card is in theory already
possible, but then the new card will operate on the old card and runtime
state object. It might also be useful to enter pySim-shell before the
card is plugged to execute some other commands for preperation before.

So lets allow to "equip" pySim-shell with a card and rs object at
runtime.

Related: SYS#5617
Change-Id: I9cf532d9da8203065463c7201e7064de6c7ab1b5
---
M pySim-shell.py
M pySim/filesystem.py
2 files changed, 84 insertions(+), 21 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/49/25549/1

diff --git a/pySim-shell.py b/pySim-shell.py
index 92b2e02..fb9a894 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -20,6 +20,7 @@
 from typing import List
 
 import json
+import traceback
 
 import cmd2
 from cmd2 import style, fg, bg
@@ -92,28 +93,63 @@
 class PysimApp(cmd2.Cmd):
 	CUSTOM_CATEGORY = 'pySim Commands'
 	def __init__(self, card, rs, script = None):
-		basic_commands = [Iso7816Commands(), PySimCommands()]
+		basic_commands = []
 		super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False,
-				 use_ipython=True, auto_load_commands=False, command_sets=basic_commands, startup_script=script)
+				 use_ipython=True, auto_load_commands=False, startup_script=script)
 		self.intro = style('Welcome to pySim-shell!', fg=fg.red)
 		self.default_category = 'pySim-shell built-in commands'
-		self.card = card
-		iccid, sw = self.card.read_iccid()
-		self.iccid = iccid
-		self.rs = rs
-		self.py_locals = { 'card': self.card, 'rs' : self.rs }
 		self.numeric_path = False
-		self.add_settable(cmd2.Settable('numeric_path', bool, 'Print File IDs instead of names',
-						  onchange_cb=self._onchange_numeric_path))
 		self.conserve_write = True
-		self.add_settable(cmd2.Settable('conserve_write', bool, 'Read and compare before write',
-						  onchange_cb=self._onchange_conserve_write))
-		self.update_prompt()
 		self.json_pretty_print = True
-		self.add_settable(cmd2.Settable('json_pretty_print', bool, 'Pretty-Print JSON output'))
 		self.apdu_trace = False
+
+		self.add_settable(cmd2.Settable('numeric_path', bool, 'Print File IDs instead of names',
+						onchange_cb=self._onchange_numeric_path))
+		self.add_settable(cmd2.Settable('json_pretty_print', bool, 'Pretty-Print JSON output'))
 		self.add_settable(cmd2.Settable('apdu_trace', bool, 'Trace and display APDUs exchanged with card',
-						  onchange_cb=self._onchange_apdu_trace))
+						onchange_cb=self._onchange_apdu_trace))
+
+		# Ensure we have a card and rs attribute
+		self.card = None
+		self.rs = None
+		self.py_locals = { 'card': self.card, 'rs' : self.rs }
+
+		self.equip(card, rs)
+
+	def equip(self, card, rs):
+		"""
+		Equip pySim-shell with the supplied card and runtime state, add (or remove) all required settables and
+		and commands to enable card operations.
+		"""
+
+		# Unequip everything from pySim-shell that would not work in unequipped state
+		if self.rs:
+			self.rs.unregister_cmds(self)
+		if 'conserve_write' in self.settables:
+			self.remove_settable('conserve_write')
+		cmd_set = self.find_commandsets(Iso7816Commands)
+		if (cmd_set != []):
+			self.unregister_command_set(cmd_set[0])
+		cmd_set = self.find_commandsets(PySimCommands)
+		if (cmd_set != []):
+			self.unregister_command_set(cmd_set[0])
+
+		self.card = card
+		self.rs = rs
+
+		# When a card object and a runtime state is present, (re)equip pySim-shell with everything that is
+		# needed to operate on cards.
+		if self.card and self.rs:
+			self.register_command_set(Iso7816Commands())
+			self.register_command_set(PySimCommands())
+			self.add_settable(cmd2.Settable('conserve_write', bool, 'Read and compare before write',
+							onchange_cb=self._onchange_conserve_write))
+			self.iccid, sw = self.card.read_iccid()
+			rs.select('MF', self)
+		else:
+			self.poutput("pySim-shell not equipped!")
+
+		self.update_prompt()
 
 	def poutput_json(self, data, force_no_pretty = False):
 		"""like cmd2.poutput() but for a JSON serializable dict."""
@@ -144,14 +180,23 @@
 			self.cmd2.poutput("<- %s: %s" % (sw, resp))
 
 	def update_prompt(self):
-		path_list = self.rs.selected_file.fully_qualified_path(not self.numeric_path)
-		self.prompt = 'pySIM-shell (%s)> ' % ('/'.join(path_list))
+		if self.rs:
+			path_list = self.rs.selected_file.fully_qualified_path(not self.numeric_path)
+			self.prompt = 'pySIM-shell (%s)> ' % ('/'.join(path_list))
+		else:
+			self.prompt = 'pySIM-shell (no card)> '
 
 	@cmd2.with_category(CUSTOM_CATEGORY)
 	def do_intro(self, _):
 		"""Display the intro banner"""
 		self.poutput(self.intro)
 
+	@cmd2.with_category(CUSTOM_CATEGORY)
+	def do_equip(self, opts):
+		"""Equip pySim-shell with card"""
+		rs, card = init_card(sl);
+		self.equip(card, rs)
+
 
 @with_default_category('pySim Commands')
 class PySimCommands(CommandSet):
@@ -533,16 +578,29 @@
 	scc = SimCardCommands(transport=sl)
 
 
-	rs, card = init_card(sl)
-	if (rs == None or card == None):
-		exit(1)
-	app = PysimApp(card, rs, opts.script)
-	rs.select('MF', app)
+	# Detect and initalize the card in the reader. This may fail when there
+	# is no card in the reader or the card is unresponsive. PysimApp is
+	# able to tolerate and recover from that.
+	try:
+		rs, card = init_card(sl)
+		app = PysimApp(card, rs, opts.script)
+	except:
+		print("Card initalization failed with an execption:")
+		print("---------------------8<---------------------")
+		traceback.print_exc()
+		print("---------------------8<---------------------")
+		print("(you may still try to recover from this manually by using the 'equip' command.)")
+		print(" it should also be noted that some readers may behave strangely when no card")
+		print(" is inserted.)")
+		print("")
+		app = PysimApp(None, None, opts.script)
 
 	# If the user supplies an ADM PIN at via commandline args authenticate
 	# immediately so that the user does not have to use the shell commands
 	pin_adm = sanitize_pin_adm(opts.pin_adm, opts.pin_adm_hex)
 	if pin_adm:
+		if not card:
+			print("Card error, cannot do ADM verification with supplied ADM pin now.")
 		try:
 			card.verify_adm(h2b(pin_adm))
 		except Exception as e:
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index 170429b..ea1fd81 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -1324,6 +1324,11 @@
             raise TypeError("Only works with BER-TLV EF")
         return self.card._scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.conserve_write)
 
+    def unregister_cmds(self, cmd_app=None):
+        """Unregister all file specific commands."""
+        if cmd_app and self.selected_file.shell_commands:
+            for c in self.selected_file.shell_commands:
+                cmd_app.unregister_command_set(c)
 
 
 

-- 
To view, visit https://gerrit.osmocom.org/c/pysim/+/25549
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I9cf532d9da8203065463c7201e7064de6c7ab1b5
Gerrit-Change-Number: 25549
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210923/674e308f/attachment.htm>


More information about the gerrit-log mailing list