mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2026-05-12 01:34:56 +00:00
eea53491de
* feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context * feat: FuriPipe * POTENTIALLY EXPLOSIVE pipe welding * fix: non-explosive welding * Revert welding * docs: furi_pipe * feat: pipe event loop integration * update f18 sdk * f18 * docs: make doxygen happy * fix: event loop not triggering when pipe attached to stdio * fix: partial stdout in pipe * allow simultaneous in and out subscription in event loop * feat: vcp i/o * feat: cli ansi stuffs and history * feat: more line editing * working but slow cli rewrite * restore previous speed after 4 days of debugging 🥲 * fix: cli_app_should_stop * fix: cli and event_loop memory leaks * style: remove commented out code * ci: fix pvs warnings * fix: unit tests, event_loop crash * ci: fix build * ci: silence pvs warning * feat: cli gpio * ci: fix formatting * Fix memory leak during event loop unsubscription * Event better memory leak fix * feat: cli completions * Merge remote-tracking branch 'origin/dev' into portasynthinca3/3928-cli-threads * merge fixups * temporarily exclude speaker_debug app * pvs and unit tests fixups * feat: commands in fals * move commands out of flash, code cleanup * ci: fix errors * fix: run commands in buffer when stopping session * speedup cli file transfer * fix f18 * separate cli_shell into modules * fix pvs warning * fix qflipper refusing to connect * remove temp debug logs * remove erroneous conclusion * Fix memory leak during event loop unsubscription * Event better memory leak fix * unit test for the fix * improve thread stdio callback signatures * pipe stdout timeout * update api symbols * fix f18, formatting * fix pvs warnings * increase stack size, hope to fix unit tests * cli completions * more key combos * commands in fals * move commands out of flash * ci: fix errors * speedup cli file transfer * merge fixups * fix f18 * cli: revert flag changes * cli: fix formatting * cli, fbt: loopback perf benchmark * thread, event_loop: subscribing to thread flags * cli: signal internal events using thread flags, improve performance * fix f18, formatting * event_loop: fix crash * storage_cli: increase write_chunk buffer size again * cli: explanation for order=0 * thread, event_loop: thread flags callback refactor * cli: increase stack size * cli: rename cli_app_should_stop -> cli_is_pipe_broken_or_is_etx_next_char * cli: use plain array instead of mlib for history * cli: prepend file name to static fns * cli: fix formatting * cli_shell: increase stack size * Now cli_shell can be customized with another motd and another command set * Added custom motd callback definition * Now user can alloc and free his own cli command set * cli_vcp can now restart shell with another command set * Help command modified to show available commands from different command sets * Api adjustement * Reworked nfc_cli to start new shell with another command set * Revert custom shell changes from vcp * Custom motd callback moved to cli_shell * Cli Shell now can be started from ongoing cli command * Help command moved to a separate function so it can be used for custom shell * Now nfc command spawns separate shell for further nfc commands * cli_shell: give up pipe to command thread * fix formatting * cli_shell: separate into toolbox * speaker_debug: fix * fix: format * Merge branch 'portasynthinca3/3928-3929-cli-fals-threads' into portasynthinca3/3965-cli_shell-toolbox * fix merge * fix. merge. * fix formatting * fix: cmd flags * fix: formatting * Added basic command descriptor structs and macros * Basic nfc commands definitions added * Nfc cli commands collection and functions added * Raw skeleton of nfc cli processor added * cli: increase default stack depth * New callbacks for ctx alloc / free added * nfc_cli moved to cli folder * Some more logic for command processor * Scanner command no works via command_processor * plugin manifest adj * Argument descriptors were removed, now only keys left * Some helper command function implemented * Command processor logic now mostly works * Added all parsers and dummy implementation of raw cmd * Now processor checks duplicated keys and treat them as errors * Some renamings * Arguments processing moved to separate function * Now command processor can reuse context of previuos command for the next one if it's allowed * can_reuse callback added for checking if context can be reused * command processor is now freed on nfc cli exit * Some cleanups * First working version of raw command * Now input data are placed directly to bit buffer * Added tag * Introduced request/response structs * Moved raw command to a separate folder * Moved some common types to header * Added protocol specific handlers for iso14a and felica * Opened felica crc header for referencing * Added handler for iso14443_3b * Opened iso15693_3_poller for referencing * Added iso15693_3 handler for raw command * NfcCliRawError enum introduced for response result * Refactored handlers implementation * Formatting functions now added as helpers * New printing result logic * Not present error value added to enum * Timeout added to raw command * Command processor now supports multivalue keys * Apdu command implementation added * NfcScanner moved to helpers and command now uses it * Helper now can format protocol names * Dump command added * Added some more functions to scanner helper * Dump main logic simplified * Dump handlers moved to protocols folder * Protocol parser added to simplify searching protocol by name * Protocol and key arguments added to dump command * Cleanups * Apdu now parses protocol using helper parser * Raw now parses protocol using helper parser * Wrong naming fix * Emulate command added to cli * Description added to action descriptor and command macros * Description field added to all commands * Removed unnecessary enum for commands * Added functions for formatting command and action info * Proper error messages and help added * Fix for unsupported single action command * Function renamed to more appropriate * Field command moved to all other commands * Cleanups * Nfc commands modified with new cli shell * Removed previous nfc_cli.c after merge * Removed nfc_cli.h header * Some renamings and cleanups * Some comments and instructions added * Some comments and instructions added * TODOs removed * Fix for missing parse callback * Added not implemented dummy for mfu actions, for now * Fix name mismatch * Remove unneeded header * Mfu command moved to separate folder, also raw info action logic added * Dictionary with id/vendors added to assets. It is used by nfc_cli_mfu_info_get_vendor function * One more unneeded header removed * Moved mfu info action to a separate file * Info action now uses sync mfu poller * mfu rdbl action added * wrbl action added for mfu command * Some formatting for rdbl command * Function for formatting mfu errors added * All mfu actions now show errors in the same way * Fix error with sync poller. Previously when read failed function returned ErrorNone, now it processes iso14a error to get proper value * Make PVS happy * Nfc cli now doesn't start if desktop app is running * Make action description look more common * Scanner now has -t key and can show detected protocol hierarchies * Apdu now checks max input payload data * Proper format * Proper error handling added to dump command * Timeout key added dump command * Fix merge issue * formatting * Pragma pack replaced with FURI_PACKED * Fix felica memory leak --------- Co-authored-by: Anna Antonenko <portasynthinca3@gmail.com> Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com> Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
389 lines
14 KiB
C
389 lines
14 KiB
C
#include "nfc_cli_command_processor.h"
|
|
#include "nfc_cli_commands.h"
|
|
#include "nfc_cli_command_base_i.h"
|
|
|
|
#include <m-string.h>
|
|
#include <args.h>
|
|
#include <hex.h>
|
|
|
|
#define TAG "NfcCliProcessor"
|
|
|
|
#define NFC_CLI_KEYS_FOUND_SIZE_BYTES (10 * sizeof(NfcCliKeyDescriptor*))
|
|
|
|
typedef enum {
|
|
NfcCliArgumentTypeShortNameKey,
|
|
NfcCliArgumentTypeShortNameKeyGroup,
|
|
NfcCliArgumentTypeLongNameKey,
|
|
|
|
NfcCliArgumentTypeUnknown
|
|
} NfcCliArgumentType;
|
|
|
|
/**
|
|
* @brief Error codes for different processing states
|
|
*/
|
|
typedef enum {
|
|
NfcCliProcessorErrorNone, /**< Command was parsed successfully and execute callback will be invoked*/
|
|
NfcCliProcessorErrorNoneButHelp, /**< There was no error, but help needs to be printed. Command wil not be executed */
|
|
NfcCliProcessorErrorActionNotFound, /**< Wrong action was passed as first command parameter */
|
|
NfcCliProcessorErrorKeyNotSupported, /**< Unsupported key was passed in arguments. Details will be printed in erro_message*/
|
|
NfcCliProcessorErrorKeyParameterInGroup, /**< Parameter which requires value was passed in group. Example: -sckd */
|
|
NfcCliProcessorErrorKeyParameterValueMissing, /**< Value is missing for the parameter which requires it */
|
|
NfcCliProcessorErrorKeyDuplication, /**< Some argument key was duplicated in input parameters */
|
|
NfcCliProcessorErrorKeyParseError, /**< Error happened during argument value parsing */
|
|
NfcCliProcessorErrorKeyRequiredMissing, /**< Some keys required for command execution is missing*/
|
|
|
|
NfcCliProcessorErrorNum
|
|
} NfcCliProcessorError;
|
|
|
|
struct NfcCliProcessorContext {
|
|
const NfcCliCommandDescriptor* cmd;
|
|
const NfcCliActionDescriptor* action;
|
|
const NfcCliKeyDescriptor** keys_found;
|
|
uint8_t total_keys_found;
|
|
uint8_t required_keys_expected;
|
|
uint8_t required_keys_found;
|
|
|
|
Nfc* nfc;
|
|
void* action_context;
|
|
|
|
FuriString* error_message;
|
|
};
|
|
|
|
static const NfcCliActionDescriptor*
|
|
nfc_cli_get_action_from_args(const NfcCliCommandDescriptor* cmd, FuriString* args) {
|
|
const NfcCliActionDescriptor* action = cmd->actions[0];
|
|
|
|
bool multiple_action_cmd = nfc_cli_command_has_multiple_actions(cmd);
|
|
if(multiple_action_cmd) {
|
|
action = NULL;
|
|
FuriString* arg_str = furi_string_alloc();
|
|
if(args_read_string_and_trim(args, arg_str)) {
|
|
action = nfc_cli_command_get_action_by_name(cmd, arg_str);
|
|
}
|
|
furi_string_free(arg_str);
|
|
}
|
|
|
|
return action;
|
|
}
|
|
|
|
static bool nfc_cli_action_can_reuse_context(
|
|
NfcCliProcessorContext* instance,
|
|
const NfcCliActionDescriptor* new_action) {
|
|
bool result = false;
|
|
do {
|
|
if(instance->action != new_action) break;
|
|
if(new_action->can_reuse == NULL) break;
|
|
result = new_action->can_reuse(instance->action_context);
|
|
} while(false);
|
|
return result;
|
|
}
|
|
|
|
static void nfc_cli_action_free(NfcCliProcessorContext* instance) {
|
|
if(instance->action && instance->action->free) {
|
|
FURI_LOG_D(TAG, "Free previous \"%s\" action context", instance->action->name);
|
|
instance->action->free(instance->action_context);
|
|
}
|
|
instance->action = NULL;
|
|
}
|
|
|
|
static NfcCliProcessorError
|
|
nfc_cli_action_alloc(NfcCliProcessorContext* instance, FuriString* args) {
|
|
const NfcCliCommandDescriptor* cmd = instance->cmd;
|
|
|
|
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
|
do {
|
|
const NfcCliActionDescriptor* action = nfc_cli_get_action_from_args(cmd, args);
|
|
if(action == NULL) {
|
|
result = NfcCliProcessorErrorActionNotFound;
|
|
furi_string_printf(instance->error_message, "Action not found");
|
|
break;
|
|
}
|
|
|
|
if(!nfc_cli_action_can_reuse_context(instance, action)) {
|
|
nfc_cli_action_free(instance);
|
|
|
|
instance->action = action;
|
|
if(action->alloc && action->free) {
|
|
FURI_LOG_D(TAG, "Allocating context for action \"%s\"", action->name);
|
|
instance->action_context = instance->action->alloc(instance->nfc);
|
|
} else if(action->alloc && (action->free == NULL)) {
|
|
FURI_LOG_W(
|
|
TAG,
|
|
"Free callback not defined for action \"%s\". Skip allocation to avoid memory leak.",
|
|
action->name);
|
|
instance->action_context = NULL;
|
|
} else {
|
|
FURI_LOG_D(TAG, "No alloc context callback for action \"%s\"", action->name);
|
|
instance->action_context = NULL;
|
|
}
|
|
} else
|
|
FURI_LOG_D(TAG, "Reusing context from previous \"%s\" action", action->name);
|
|
|
|
memset(instance->keys_found, 0, NFC_CLI_KEYS_FOUND_SIZE_BYTES);
|
|
instance->required_keys_expected = nfc_cli_action_get_required_keys_count(action);
|
|
instance->required_keys_found = 0;
|
|
instance->total_keys_found = 0;
|
|
} while(false);
|
|
|
|
return result;
|
|
}
|
|
|
|
static NfcCliArgumentType nfc_cli_get_argument_type(FuriString* argument) {
|
|
size_t arg_len = furi_string_size(argument);
|
|
NfcCliArgumentType type = NfcCliArgumentTypeUnknown;
|
|
|
|
if(arg_len > 2) {
|
|
char ch1 = furi_string_get_char(argument, 0);
|
|
char ch2 = furi_string_get_char(argument, 1);
|
|
if(ch1 == '-') {
|
|
type = (ch2 == '-') ? NfcCliArgumentTypeLongNameKey :
|
|
NfcCliArgumentTypeShortNameKeyGroup;
|
|
}
|
|
} else if(arg_len == 2) {
|
|
char ch1 = furi_string_get_char(argument, 0);
|
|
type = (ch1 == '-') ? NfcCliArgumentTypeShortNameKey : NfcCliArgumentTypeUnknown;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static bool
|
|
nfc_cli_check_duplicate_keys(NfcCliProcessorContext* instance, const NfcCliKeyDescriptor* key) {
|
|
bool result = false;
|
|
for(size_t i = 0; i < instance->total_keys_found; i++) {
|
|
const NfcCliKeyDescriptor* buf = instance->keys_found[i];
|
|
if(buf != key) continue;
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void nfc_cli_trim_multivalue_arg(FuriString* args, FuriString* value) {
|
|
furi_string_set(value, args);
|
|
size_t index = furi_string_search_char(value, '-', 0);
|
|
if(index != STRING_FAILURE) {
|
|
furi_string_left(value, index);
|
|
furi_string_right(args, index);
|
|
} else {
|
|
furi_string_reset(args);
|
|
}
|
|
}
|
|
|
|
static NfcCliProcessorError nfc_cli_parse_single_key(
|
|
NfcCliProcessorContext* instance,
|
|
FuriString* argument,
|
|
FuriString* args,
|
|
bool from_group) {
|
|
FuriString* value_str = furi_string_alloc();
|
|
|
|
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
|
do {
|
|
const NfcCliKeyDescriptor* key =
|
|
nfc_cli_action_get_key_descriptor(instance->action, argument);
|
|
if(key == NULL) {
|
|
if(furi_string_equal_str(argument, "h"))
|
|
result = NfcCliProcessorErrorNoneButHelp;
|
|
else {
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Key \'%s\' is not supported",
|
|
furi_string_get_cstr(argument));
|
|
result = NfcCliProcessorErrorKeyNotSupported;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(key->features.parameter && from_group) {
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Parameter key \'%s\' can\'t be grouped",
|
|
furi_string_get_cstr(argument));
|
|
result = NfcCliProcessorErrorKeyParameterInGroup;
|
|
break;
|
|
}
|
|
|
|
if(nfc_cli_check_duplicate_keys(instance, key)) {
|
|
furi_string_printf(
|
|
instance->error_message, "Duplicated key \'%s\'", furi_string_get_cstr(argument));
|
|
result = NfcCliProcessorErrorKeyDuplication;
|
|
break;
|
|
}
|
|
|
|
if(key->features.multivalue && !key->features.parameter) break;
|
|
if(key->features.multivalue) {
|
|
nfc_cli_trim_multivalue_arg(args, value_str);
|
|
FURI_LOG_D(TAG, "Multivalue: %s", furi_string_get_cstr(value_str));
|
|
} else if(key->features.parameter && !args_read_string_and_trim(args, value_str)) {
|
|
result = NfcCliProcessorErrorKeyParameterValueMissing;
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Missing value for \'%s\'",
|
|
furi_string_get_cstr(argument));
|
|
break;
|
|
}
|
|
|
|
if(key->parse == NULL) {
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Parse callback for key \'%s\' not defined",
|
|
furi_string_get_cstr(argument));
|
|
result = NfcCliProcessorErrorKeyParseError;
|
|
break;
|
|
}
|
|
|
|
FURI_LOG_D(TAG, "Parsing key \"%s\"", furi_string_get_cstr(argument));
|
|
if(!key->parse(value_str, instance->action_context)) {
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Unable to parse value \'%s\' for key \'%s\'",
|
|
furi_string_get_cstr(value_str),
|
|
furi_string_get_cstr(argument));
|
|
result = NfcCliProcessorErrorKeyParseError;
|
|
break;
|
|
}
|
|
|
|
instance->keys_found[instance->total_keys_found] = key;
|
|
instance->total_keys_found++;
|
|
if(key->features.required) instance->required_keys_found++;
|
|
} while(false);
|
|
furi_string_free(value_str);
|
|
|
|
return result;
|
|
}
|
|
|
|
static NfcCliProcessorError
|
|
nfc_cli_parse_group_key(NfcCliProcessorContext* instance, FuriString* argument) {
|
|
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
|
FURI_LOG_D(TAG, "Parsing key group\"%s\"", furi_string_get_cstr(argument));
|
|
|
|
FuriString* arg_buf = furi_string_alloc();
|
|
for(size_t i = 0; i < furi_string_size(argument); i++) {
|
|
furi_string_set_n(arg_buf, argument, i, 1);
|
|
result = nfc_cli_parse_single_key(instance, arg_buf, NULL, true);
|
|
if(result != NfcCliProcessorErrorNone) break;
|
|
}
|
|
furi_string_free(arg_buf);
|
|
|
|
return result;
|
|
}
|
|
|
|
static NfcCliProcessorError nfc_cli_parse_argument(
|
|
NfcCliProcessorContext* instance,
|
|
FuriString* argument,
|
|
FuriString* args) {
|
|
NfcCliArgumentType type = nfc_cli_get_argument_type(argument);
|
|
|
|
furi_string_trim(argument, "-");
|
|
|
|
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
|
|
|
if(type == NfcCliArgumentTypeShortNameKeyGroup)
|
|
result = nfc_cli_parse_group_key(instance, argument);
|
|
else if((type == NfcCliArgumentTypeShortNameKey) || (type == NfcCliArgumentTypeLongNameKey)) {
|
|
result = nfc_cli_parse_single_key(instance, argument, args, false);
|
|
} else if(type == NfcCliArgumentTypeUnknown) { //-V547
|
|
result = NfcCliProcessorErrorKeyNotSupported;
|
|
furi_string_printf(
|
|
instance->error_message,
|
|
"Key \'%s\' is not supported",
|
|
furi_string_get_cstr(argument));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static NfcCliProcessorError
|
|
nfc_cli_process_arguments(NfcCliProcessorContext* instance, FuriString* args) {
|
|
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
|
|
|
FuriString* argument = furi_string_alloc();
|
|
while(args_read_string_and_trim(args, argument)) {
|
|
result = nfc_cli_parse_argument(instance, argument, args);
|
|
if(result != NfcCliProcessorErrorNone) break;
|
|
}
|
|
furi_string_free(argument);
|
|
|
|
if((result == NfcCliProcessorErrorNone) &&
|
|
(instance->required_keys_expected != instance->required_keys_found)) {
|
|
furi_string_printf(instance->error_message, "Some required keys missing");
|
|
result = NfcCliProcessorErrorKeyRequiredMissing;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static inline void nfc_cli_command_process_error(
|
|
const NfcCliProcessorContext* instance,
|
|
NfcCliProcessorError error) {
|
|
do {
|
|
if(error == NfcCliProcessorErrorNone) break;
|
|
|
|
if(error != NfcCliProcessorErrorNoneButHelp)
|
|
printf(
|
|
ANSI_FG_RED "Error: %s\r\n" ANSI_RESET,
|
|
furi_string_get_cstr(instance->error_message));
|
|
|
|
if(error == NfcCliProcessorErrorActionNotFound)
|
|
nfc_cli_command_format_info(instance->cmd, instance->error_message);
|
|
else
|
|
nfc_cli_action_format_info(instance->action, instance->error_message);
|
|
|
|
printf("\n%s", furi_string_get_cstr(instance->error_message));
|
|
} while(false);
|
|
}
|
|
|
|
void nfc_cli_command_processor_run(
|
|
const NfcCliCommandDescriptor* cmd,
|
|
PipeSide* pipe,
|
|
FuriString* args,
|
|
void* context) {
|
|
furi_assert(pipe);
|
|
furi_assert(cmd);
|
|
furi_assert(args);
|
|
NfcCliProcessorContext* instance = context;
|
|
furi_string_reset(instance->error_message);
|
|
|
|
NfcCliProcessorError error = NfcCliProcessorErrorNone;
|
|
instance->cmd = cmd;
|
|
do {
|
|
error = nfc_cli_action_alloc(instance, args);
|
|
if(error != NfcCliProcessorErrorNone) break;
|
|
|
|
error = nfc_cli_process_arguments(instance, args);
|
|
if(error != NfcCliProcessorErrorNone) break;
|
|
|
|
if(instance->action && instance->action->execute) {
|
|
instance->action->execute(pipe, instance->action_context);
|
|
} else {
|
|
FURI_LOG_W(TAG, "Action execute callback missing");
|
|
}
|
|
} while(false);
|
|
|
|
nfc_cli_command_process_error(instance, error);
|
|
}
|
|
|
|
NfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc) {
|
|
furi_assert(nfc);
|
|
NfcCliProcessorContext* instance = malloc(sizeof(NfcCliProcessorContext));
|
|
instance->nfc = nfc;
|
|
instance->keys_found = malloc(NFC_CLI_KEYS_FOUND_SIZE_BYTES);
|
|
instance->total_keys_found = 0;
|
|
instance->required_keys_found = 0;
|
|
instance->required_keys_expected = 0;
|
|
|
|
instance->error_message = furi_string_alloc();
|
|
return instance;
|
|
}
|
|
|
|
void nfc_cli_command_processor_free(NfcCliProcessorContext* instance) {
|
|
furi_assert(instance);
|
|
nfc_cli_action_free(instance);
|
|
free(instance->keys_found);
|
|
furi_string_free(instance->error_message);
|
|
|
|
instance->nfc = NULL;
|
|
free(instance);
|
|
}
|