Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework cli #168

Merged
merged 32 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bb0ad66
cli: reorder args
doegox Oct 6, 2023
465ada3
cli: change hw chipid, hw address and hw mode
doegox Oct 6, 2023
7ae3820
cli: change hw slot kick
doegox Oct 6, 2023
1ce506a
cli: change hw slot list
doegox Oct 6, 2023
3022e05
help_str
doegox Oct 6, 2023
0c6abbe
cli: -s mandatory for slot change, sense_type now --hf/--lf, slot opt…
doegox Oct 7, 2023
3aa73a2
cli: metavar, simplify enums, replace tag types int by keywords
doegox Oct 7, 2023
6cad966
cli: hw slot enable/disable/store
doegox Oct 7, 2023
0f730af
Activate automatically reader mode
doegox Oct 7, 2023
18a1770
cli: hw settings animation/bleclearbonds, hw factory_reset and hw set…
doegox Oct 8, 2023
47742d5
cli: hw settings blepair, hw settings reset + --force, hw settings an…
doegox Oct 8, 2023
d127f0a
cli: blepair fix bug
doegox Oct 8, 2023
cd51061
cli: hw slot enable: bugfix default slot
doegox Oct 8, 2023
a47e9b8
cli: remove unused TagSenseType list
doegox Oct 8, 2023
efd7405
cli: hw settings btnpress WIP
doegox Oct 8, 2023
45deb30
cli: hw settings btnpress WIP
doegox Oct 8, 2023
1da470b
cli: hw settings btnpress
doegox Oct 8, 2023
dcebc89
cli: Command enum
doegox Oct 8, 2023
831c401
cli: hw raw
doegox Oct 8, 2023
8fc45a6
cli: hf 14a raw
doegox Oct 8, 2023
dd5ff24
hf mf nested
doegox Oct 8, 2023
deb6684
cli: enum for key type A/B, prepare hf mf auth args
doegox Oct 8, 2023
2f4d84e
cli: clean enums
doegox Oct 8, 2023
8080c9b
lf em 410x econfig
doegox Oct 8, 2023
2d3538f
cli: add support for after_exec
doegox Oct 8, 2023
336ec82
cli: hf mf eload/esave
doegox Oct 8, 2023
a2121de
cli: hf mf elog
doegox Oct 8, 2023
75e368d
cli: hf mf econfig, hf mfu econfig (wip), fix dumphelp
doegox Oct 9, 2023
9106dea
clean args classes
doegox Oct 9, 2023
4927e01
cli: one root CLITree, enhance dump_help
doegox Oct 9, 2023
b6d2bad
cli: simplify exit conditions
doegox Oct 9, 2023
61cc4f5
cli: color help and add epilog example
doegox Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...

## [unreleased][unreleased]
- Added colors to CLI help (@doegox)
- Changed massively CLI, cf https://github.com/RfidResearchGroup/ChameleonUltra/issues/164#issue-1930580576 (@doegox)
- Changed CLI help: lists display and now all commands support `-h` (@doegox)
- Added button action to show battery level (@doegox)
- Added GUI Page docs (@GameTec-live)
Expand Down
12 changes: 6 additions & 6 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ Notes:
* Response: 4+N*8 bytes: `uid[4]` followed by N tuples of `nt[4]|nt_enc[4]`. All values as U32.
* CLI: cf `hf mf nested` on static nonce tag
### 2004: MF1_DARKSIDE_ACQUIRE
* Command: 4 bytes: `type_target|block_target|first_recover|sync_max`
* Command: 4 bytes: `type_target|block_target|first_recover|sync_max`. Type=0x60 for key A, 0x61 for key B.
* Response: 1 byte if Darkside failed, according to `mf1_darkside_status_t` enum,
else 33 bytes `darkside_status|uid[4]|nt1[4]|par[8]|ks1[8]|nr[4]|ar[4]`
* `darkside_status`
Expand All @@ -253,29 +253,29 @@ Notes:
* `ar[4]` U32
* CLI: cf `hf mf darkside`
### 2005: MF1_DETECT_NT_DIST
* Command: 8 bytes: `type_known|block_known|key_known[6]`. Key as 6 bytes.
* Command: 8 bytes: `type_known|block_known|key_known[6]`. Key as 6 bytes. Type=0x60 for key A, 0x61 for key B.
* Response: 8 bytes: `uid[4]|dist[4]`
* `uid[4]` U32 (format expected by `nested` tool)
* `dist[4]` U32
* CLI: cf `hf mf nested`
### 2006: MF1_NESTED_ACQUIRE
* Command: 10 bytes: `type_known|block_known|key_known[6]|type_target|block_target`. Key as 6 bytes.
* Command: 10 bytes: `type_known|block_known|key_known[6]|type_target|block_target`. Key as 6 bytes. Type=0x60 for key A, 0x61 for key B.
* Response: N*9 bytes: N tuples of `nt[4]|nt_enc[4]|par`
* `nt[4]` U32
* `nt_enc[4]` U32
* `par`
* CLI: cf `hf mf nested`
### 2007: MF1_AUTH_ONE_KEY_BLOCK
* Command: 8 bytes: `type|block|key[6]`. Key as 6 bytes.
* Command: 8 bytes: `type|block|key[6]`. Key as 6 bytes. Type=0x60 for key A, 0x61 for key B.
* Response: no data
* Status will be `HF_TAG_OK` if auth succeeded, else `MF_ERR_AUTH`
* CLI: cf `hf mf nested`
### 2008: MF1_READ_ONE_BLOCK
* Command: 8 bytes: `type|block|key[6]`. Key as 6 bytes.
* Command: 8 bytes: `type|block|key[6]`. Key as 6 bytes. Type=0x60 for key A, 0x61 for key B.
* Response: 16 bytes: `block_data[16]`
* CLI: cf `hf mf rdbl`
### 2009: MF1_WRITE_ONE_BLOCK
* Command: 24 bytes: `type|block|key[6]|block_data[16]`. Key as 6 bytes.
* Command: 24 bytes: `type|block|key[6]|block_data[16]`. Key as 6 bytes. Type=0x60 for key A, 0x61 for key B.
* Response: no data
* CLI: cf `hf mf wrbl`
### 2010: HF14A_RAW
Expand Down
2 changes: 1 addition & 1 deletion firmware/Makefile.defs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ APP_FW_VER_MAJOR := $(word 1,$(subst ., ,$(APP_FW_SEMVER)))
APP_FW_VER_MINOR := $(word 2,$(subst ., ,$(APP_FW_SEMVER)))

# Enable NRF_LOG on SWO pin as UART TX
NRF_LOG_UART_ON_SWO_ENABLED := 0
NRF_LOG_UART_ON_SWO_ENABLED := 1

# Enable SDK validation checks
SDK_VALIDATION := 0
113 changes: 30 additions & 83 deletions software/script/chameleon_cli_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
import colorama
import chameleon_cli_unit
import chameleon_utils
import os
import pathlib
import prompt_toolkit
from datetime import datetime
from prompt_toolkit.formatted_text import ANSI
from prompt_toolkit.history import FileHistory

# Colorama shorthands
CR = colorama.Fore.RED
CG = colorama.Fore.GREEN
CB = colorama.Fore.BLUE
CC = colorama.Fore.CYAN
CY = colorama.Fore.YELLOW
CM = colorama.Fore.MAGENTA
C0 = colorama.Style.RESET_ALL

ULTRA = r"""
Expand All @@ -43,43 +43,13 @@
"""


def dump_help(cmd_node, depth=0, dump_cmd_groups=False, dump_description=False):
if cmd_node.cls:
cmd_title = f"{CG}{cmd_node.fullname}{C0}"
if dump_description:
print(f" {cmd_title}".ljust(37) + f"{cmd_node.help_text}")
else:
print(f" {cmd_title}".ljust(37), end="")
p = cmd_node.cls().args_parser()
assert p is not None
p.prog = ""
usage = p.format_usage().removeprefix("usage: ").rstrip()
if usage != "[-h]":
usage = usage.removeprefix("[-h] ")
if dump_description:
print(f"{CG}{C0}".ljust(37), end="")
print(f"{CY}{usage}{C0}")
else:
print("")
else:
if dump_cmd_groups:
cmd_title = f"{CY}{cmd_node.fullname}{C0}"
if dump_description:
print(f" {cmd_title}".ljust(37) + f"{{ {cmd_node.help_text}... }}")
else:
print(f" {cmd_title}")
for child in cmd_node.children:
dump_help(child, depth + 1, dump_cmd_groups, dump_description)


class ChameleonCLI:
"""
CLI for chameleon
"""

def __init__(self):
self.completer = chameleon_utils.CustomNestedCompleter.from_nested_dict(
chameleon_cli_unit.root_commands)
self.completer = chameleon_utils.CustomNestedCompleter.from_clitree(chameleon_cli_unit.root)
self.session = prompt_toolkit.PromptSession(completer=self.completer,
history=FileHistory(pathlib.Path.home() / ".chameleon_history"))

Expand Down Expand Up @@ -132,7 +102,6 @@ def startCLI(self):
raise Exception("This script requires at least Python 3.9")

self.print_banner()
closing = False
cmd_strs = []
while True:
if cmd_strs:
Expand All @@ -145,64 +114,34 @@ def startCLI(self):
cmd_strs = cmd_str.replace(
"\r\n", "\n").replace("\r", "\n").split("\n")
cmd_str = cmd_strs.pop(0)
if cmd_str == "":
continue
except EOFError:
closing = True
cmd_str = 'exit'
except KeyboardInterrupt:
closing = True

if closing or cmd_str in ["exit", "quit", "q", "e"]:
print("Bye, thank you. ^.^ ")
self.device_com.close()
sys.exit(996)
elif cmd_str == "clear":
os.system('clear' if os.name == 'posix' else 'cls')
continue
elif cmd_str == "dumphelp":
for _, cmd_node in chameleon_cli_unit.root_commands.items():
dump_help(cmd_node)
continue
elif cmd_str == "":
continue
cmd_str = 'exit'

# look for alternate exit
if cmd_str in ["quit", "q", "e"]:
cmd_str = 'exit'

# look for alternate comments
if cmd_str[0] in ";#%":
cmd_str = 'rem ' + cmd_str[1:].lstrip()

# parse cmd
argv = cmd_str.split()
root_cmd = argv[0]
# look for comments
if root_cmd == "rem" or root_cmd[0] in ";#%":
# precision: second
# iso_timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
# precision: nanosecond (note that the comment will take some time too, ~75ns, check your system)
iso_timestamp = datetime.utcnow().isoformat() + 'Z'
if root_cmd[0] in ";#%":
comment = ' '.join([root_cmd[1:]]+argv[1:]).strip()
else:
comment = ' '.join(argv[1:]).strip()
print(f"{iso_timestamp} remark: {comment}")
continue
if root_cmd not in chameleon_cli_unit.root_commands:
# No matching command group
print("".ljust(18, "-") + "".ljust(10) + "".ljust(30, "-"))
for cmd_name, cmd_node in chameleon_cli_unit.root_commands.items():
print(f" - {CG}{cmd_name}{C0}".ljust(37) + f"{{ {cmd_node.help_text}... }}")
print(f" - {CG}clear{C0}".ljust(37) + "Clear screen")
print(f" - {CG}exit{C0}".ljust(37) + "Exit program")
print(f" - {CG}rem ...{C0}".ljust(37) + "Display a comment with a timestamp")
continue

tree_node, arg_list = self.get_cmd_node(
chameleon_cli_unit.root_commands[root_cmd], argv[1:])

tree_node, arg_list = self.get_cmd_node(chameleon_cli_unit.root, argv)
if not tree_node.cls:
# Found tree node is a group without an implementation, print children
print("".ljust(18, "-") + "".ljust(10) + "".ljust(30, "-"))
for child in tree_node.children:
cmd_title = f"{CG}{child.name}{C0}"
if not child.cls:
help_line = (f" - {cmd_title}".ljust(37)
) + f"{{ {child.help_text}... }}"
help_line = (f" - {cmd_title}".ljust(37)) + f"{{ {child.help_text}... }}"
else:
help_line = (f" - {cmd_title}".ljust(37)
) + f"{child.help_text}"
help_line = (f" - {cmd_title}".ljust(37)) + f"{child.help_text}"
print(help_line)
continue

Expand All @@ -216,8 +155,8 @@ def startCLI(self):
try:
args_parse_result = args.parse_args(arg_list)
except chameleon_utils.ArgsParserError as e:
args.print_usage()
print(str(e).strip(), end="\n\n")
args.print_help()
print(f'{CY}'+str(e).strip()+f'{C0}', end="\n\n")
continue
except chameleon_utils.ParserExitIntercept:
# don't exit process.
Expand All @@ -227,8 +166,16 @@ def startCLI(self):
if not unit.before_exec(args_parse_result):
continue

# start process cmd
unit.on_exec(args_parse_result)
# start process cmd, delay error to call after_exec firstly
error = None
try:
unit.on_exec(args_parse_result)
except Exception as e:
error = e
unit.after_exec(args_parse_result)
if error is not None:
raise error

except (chameleon_utils.UnexpectedResponseError, chameleon_utils.ArgsParserError) as e:
print(f"{CR}{str(e)}{C0}")
except Exception:
Expand Down
Loading
Loading