From 84beb9b23e3cd1b9eb42a50038cefc2941b2c4db Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 25 Mar 2024 19:35:38 +0300 Subject: [PATCH 1/2] JS CLI command (#3539) * js command * made the js command exit when there's an error * JS CLI: moved to js_app * JS: abortable cli invocations * JS: less debug logging in console logs, fix storage descriptor leak in cli Co-authored-by: Milk-Cool Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- applications/system/application.fam | 1 + applications/system/js_app/application.fam | 7 ++ applications/system/js_app/js_app.c | 85 +++++++++++++++++++++- applications/system/js_app/js_modules.c | 5 +- applications/system/js_app/js_thread.c | 4 +- 5 files changed, 98 insertions(+), 4 deletions(-) diff --git a/applications/system/application.fam b/applications/system/application.fam index 2d4dfcbb82..095ca1ab2c 100644 --- a/applications/system/application.fam +++ b/applications/system/application.fam @@ -6,6 +6,7 @@ App( "updater_app", "storage_move_to_sd", "js_app", + "js_app_start", # "archive", ], ) diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 5716234dc3..114bec55f8 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -8,6 +8,13 @@ App( order=0, ) +App( + appid="js_app_start", + apptype=FlipperAppType.STARTUP, + entry_point="js_app_on_system_start", + order=160, +) + App( appid="js_dialog", apptype=FlipperAppType.PLUGIN, diff --git a/applications/system/js_app/js_app.c b/applications/system/js_app/js_app.c index e99cc32498..b389033122 100644 --- a/applications/system/js_app/js_app.c +++ b/applications/system/js_app/js_app.c @@ -4,6 +4,7 @@ #include "js_app_i.h" #include #include +#include #define TAG "JS app" @@ -128,4 +129,86 @@ int32_t js_app(void* arg) { js_app_free(app); return 0; -} //-V773 \ No newline at end of file +} //-V773 + +typedef struct { + Cli* cli; + FuriSemaphore* exit_sem; +} JsCliContext; + +static void js_cli_print(JsCliContext* ctx, const char* msg) { + cli_write(ctx->cli, (uint8_t*)msg, strlen(msg)); +} + +static void js_cli_exit(JsCliContext* ctx) { + furi_check(furi_semaphore_release(ctx->exit_sem) == FuriStatusOk); +} + +static void js_cli_callback(JsThreadEvent event, const char* msg, void* context) { + JsCliContext* ctx = context; + switch(event) { + case JsThreadEventError: + js_cli_print(ctx, "---- ERROR ----\r\n"); + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + break; + case JsThreadEventErrorTrace: + js_cli_print(ctx, "Trace:\r\n"); + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + + js_cli_exit(ctx); // Exit when an error occurs + break; + case JsThreadEventPrint: + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + break; + case JsThreadEventDone: + js_cli_print(ctx, "Script done!\r\n"); + + js_cli_exit(ctx); + break; + } +} + +void js_cli_execute(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + + const char* path = furi_string_get_cstr(args); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(furi_string_size(args) == 0) { + printf("Usage:\r\njs \r\n"); + break; + } + + if(!storage_file_exists(storage, path)) { + printf("Can not open file %s\r\n", path); + break; + } + + JsCliContext ctx = {.cli = cli}; + ctx.exit_sem = furi_semaphore_alloc(1, 0); + + printf("Running script %s, press CTRL+C to stop\r\n", path); + JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx); + + while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) { + if(cli_cmd_interrupt_received(cli)) break; + } + + js_thread_stop(js_thread); + furi_semaphore_free(ctx.exit_sem); + } while(false); + + furi_record_close(RECORD_STORAGE); +} + +void js_app_on_system_start(void) { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute, NULL); + furi_record_close(RECORD_CLI); +#endif +} diff --git a/applications/system/js_app/js_modules.c b/applications/system/js_app/js_modules.c index 0e2d6bf25f..fa53328845 100644 --- a/applications/system/js_app/js_modules.c +++ b/applications/system/js_app/js_modules.c @@ -5,6 +5,9 @@ #define TAG "JS modules" +// Absolute path is used to make possible plugin load from CLI +#define MODULES_PATH "/ext/apps_data/js_app/plugins" + typedef struct { JsModeConstructor create; JsModeDestructor destroy; @@ -81,7 +84,7 @@ mjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_le // External module load if(!module_found) { FuriString* module_path = furi_string_alloc(); - furi_string_printf(module_path, "%s/js_%s.fal", APP_DATA_PATH("plugins"), name); + furi_string_printf(module_path, "%s/js_%s.fal", MODULES_PATH, name); FURI_LOG_I(TAG, "Loading external module %s", furi_string_get_cstr(module_path)); do { uint32_t plugin_cnt_last = plugin_manager_get_count(modules->plugin_manager); diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 5ca3654041..759d63b0e3 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -44,12 +44,12 @@ static void js_print(struct mjs* mjs) { FuriString* msg_str = furi_string_alloc(); js_str_print(msg_str, mjs); - printf("%s\r\n", furi_string_get_cstr(msg_str)); - JsThread* worker = mjs_get_context(mjs); furi_assert(worker); if(worker->app_callback) { worker->app_callback(JsThreadEventPrint, furi_string_get_cstr(msg_str), worker->context); + } else { + FURI_LOG_D(TAG, "%s\r\n", furi_string_get_cstr(msg_str)); } furi_string_free(msg_str); From 8ec1c74baf51809f00f463c2df0564b656d34207 Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 25 Mar 2024 19:48:18 +0100 Subject: [PATCH 2/2] SavedStruct: Introduce saved_struct_get_metadata (#3392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SavedStruct: Replace saved_struct_get_payload_size with saved_struct_get_metadata This new function can obtain the magic value, version and payload size all at once. This makes the function useful e.g. for backwards compatibility. * SavedStruct: const-qualify saved_struct_save * Toolbox: add saved struct documentation Co-authored-by: あく --- .../services/bt/bt_service/bt_keys_storage.c | 13 +++-- applications/services/bt/bt_settings.c | 2 +- applications/services/bt/bt_settings.h | 2 +- .../services/expansion/expansion_settings.c | 2 +- .../services/expansion/expansion_settings.h | 2 +- lib/toolbox/saved_struct.c | 46 +++++++++-------- lib/toolbox/saved_struct.h | 49 +++++++++++++++++-- targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 9 files changed, 86 insertions(+), 38 deletions(-) diff --git a/applications/services/bt/bt_service/bt_keys_storage.c b/applications/services/bt/bt_service/bt_keys_storage.c index 215f19a89c..cf06d663ba 100644 --- a/applications/services/bt/bt_service/bt_keys_storage.c +++ b/applications/services/bt/bt_service/bt_keys_storage.c @@ -72,16 +72,19 @@ bool bt_keys_storage_load(BtKeysStorage* instance) { bool loaded = false; do { // Get payload size + uint8_t magic = 0, version = 0; size_t payload_size = 0; - if(!saved_struct_get_payload_size( - furi_string_get_cstr(instance->file_path), - BT_KEYS_STORAGE_MAGIC, - BT_KEYS_STORAGE_VERSION, - &payload_size)) { + if(!saved_struct_get_metadata( + furi_string_get_cstr(instance->file_path), &magic, &version, &payload_size)) { FURI_LOG_E(TAG, "Failed to read payload size"); break; } + if(magic != BT_KEYS_STORAGE_MAGIC || version != BT_KEYS_STORAGE_VERSION) { + FURI_LOG_E(TAG, "Saved data version is mismatched"); + break; + } + if(payload_size > instance->nvm_sram_buff_size) { FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer"); break; diff --git a/applications/services/bt/bt_settings.c b/applications/services/bt/bt_settings.c index 1eaf6c7d70..8a505f9e3f 100644 --- a/applications/services/bt/bt_settings.c +++ b/applications/services/bt/bt_settings.c @@ -15,7 +15,7 @@ bool bt_settings_load(BtSettings* bt_settings) { BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION); } -bool bt_settings_save(BtSettings* bt_settings) { +bool bt_settings_save(const BtSettings* bt_settings) { furi_assert(bt_settings); return saved_struct_save( diff --git a/applications/services/bt/bt_settings.h b/applications/services/bt/bt_settings.h index 9ed8be89c4..da43e14987 100644 --- a/applications/services/bt/bt_settings.h +++ b/applications/services/bt/bt_settings.h @@ -15,7 +15,7 @@ typedef struct { bool bt_settings_load(BtSettings* bt_settings); -bool bt_settings_save(BtSettings* bt_settings); +bool bt_settings_save(const BtSettings* bt_settings); #ifdef __cplusplus } diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c index 586bf6e9cf..691c454a57 100644 --- a/applications/services/expansion/expansion_settings.c +++ b/applications/services/expansion/expansion_settings.c @@ -19,7 +19,7 @@ bool expansion_settings_load(ExpansionSettings* settings) { EXPANSION_SETTINGS_VERSION); } -bool expansion_settings_save(ExpansionSettings* settings) { +bool expansion_settings_save(const ExpansionSettings* settings) { furi_assert(settings); return saved_struct_save( EXPANSION_SETTINGS_PATH, diff --git a/applications/services/expansion/expansion_settings.h b/applications/services/expansion/expansion_settings.h index e7663f1b95..38e9f8d025 100644 --- a/applications/services/expansion/expansion_settings.h +++ b/applications/services/expansion/expansion_settings.h @@ -36,7 +36,7 @@ bool expansion_settings_load(ExpansionSettings* settings); * @param[in] settings pointer to an ExpansionSettings instance to save settings from. * @returns true if the settings were successfully saved, false otherwise. */ -bool expansion_settings_save(ExpansionSettings* settings); +bool expansion_settings_save(const ExpansionSettings* settings); #ifdef __cplusplus } diff --git a/lib/toolbox/saved_struct.c b/lib/toolbox/saved_struct.c index e96d5b6cbf..db7e4de6e4 100644 --- a/lib/toolbox/saved_struct.c +++ b/lib/toolbox/saved_struct.c @@ -13,7 +13,12 @@ typedef struct { uint32_t timestamp; } SavedStructHeader; -bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version) { +bool saved_struct_save( + const char* path, + const void* data, + size_t size, + uint8_t magic, + uint8_t version) { furi_check(path); furi_check(data); furi_check(size); @@ -35,7 +40,7 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, if(result) { // Calculate checksum uint8_t checksum = 0; - uint8_t* source = data; + const uint8_t* source = data; for(size_t i = 0; i < size; i++) { checksum += source[i]; } @@ -63,6 +68,10 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, } bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version) { + furi_check(path); + furi_check(data); + furi_check(size); + FURI_LOG_I(TAG, "Loading \"%s\"", path); SavedStructHeader header; @@ -126,13 +135,12 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, return result; } -bool saved_struct_get_payload_size( +bool saved_struct_get_metadata( const char* path, - uint8_t magic, - uint8_t version, + uint8_t* magic, + uint8_t* version, size_t* payload_size) { furi_check(path); - furi_check(payload_size); SavedStructHeader header; Storage* storage = furi_record_open(RECORD_STORAGE); @@ -146,26 +154,22 @@ bool saved_struct_get_payload_size( break; } - size_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader)); - if(bytes_count != sizeof(SavedStructHeader)) { + if(storage_file_read(file, &header, sizeof(SavedStructHeader)) != + sizeof(SavedStructHeader)) { FURI_LOG_E(TAG, "Failed to read header"); break; } - if((header.magic != magic) || (header.version != version)) { - FURI_LOG_E( - TAG, - "Magic(%d != %d) or Version(%d != %d) mismatch of file \"%s\"", - header.magic, - magic, - header.version, - version, - path); - break; + if(magic) { + *magic = header.magic; + } + if(version) { + *version = header.version; + } + if(payload_size) { + uint64_t file_size = storage_file_size(file); + *payload_size = file_size - sizeof(SavedStructHeader); } - - uint64_t file_size = storage_file_size(file); - *payload_size = file_size - sizeof(SavedStructHeader); result = true; } while(false); diff --git a/lib/toolbox/saved_struct.h b/lib/toolbox/saved_struct.h index 9ce836564b..5ce53d3cdf 100644 --- a/lib/toolbox/saved_struct.h +++ b/lib/toolbox/saved_struct.h @@ -1,3 +1,8 @@ +/** + * @file saved_struct.h + * @brief SavedStruct - data serialization/de-serialization + * + */ #pragma once #include @@ -8,14 +13,50 @@ extern "C" { #endif +/** Load data from the file in saved structure format + * + * @param[in] path The path to the file + * @param[out] data Pointer to the memory where to load data + * @param[in] size The size of the data + * @param[in] magic The magic to embed into metadata + * @param[in] version The version to embed into metadata + * + * @return true on success, false otherwise + */ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); -bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); - -bool saved_struct_get_payload_size( +/** Save data in saved structure format + * + * @param[in] path The path to the file + * @param[in] data Pointer to the memory where data + * @param[in] size The size of the data + * @param[in] magic The magic to embed into metadata + * @param[in] version The version to embed into metadata + * + * @return true on success, false otherwise + */ +bool saved_struct_save( const char* path, + const void* data, + size_t size, uint8_t magic, - uint8_t version, + uint8_t version); + +/** Get SavedStructure file metadata + * + * @param[in] path The path to the file + * @param[out] magic Pointer to store magic or NULL if you don't need it + * @param[out] version Pointer to store version or NULL if you don't need + * it + * @param[out] payload_size Pointer to store payload size or NULL if you don't + * need it + * + * @return true on success, false otherwise + */ +bool saved_struct_get_metadata( + const char* path, + uint8_t* magic, + uint8_t* version, size_t* payload_size); #ifdef __cplusplus diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 95879cde6f..7a81367059 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -2303,9 +2303,9 @@ Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char* -Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_get_metadata,_Bool,"const char*, uint8_t*, uint8_t*, size_t*" Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" -Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, const void*, size_t, uint8_t, uint8_t" Function,-,scalbln,double,"double, long int" Function,-,scalblnf,float,"float, long int" Function,-,scalblnl,long double,"long double, long" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e0b7f68183..bfc0507995 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2866,9 +2866,9 @@ Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char* -Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_get_metadata,_Bool,"const char*, uint8_t*, uint8_t*, size_t*" Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" -Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, const void*, size_t, uint8_t, uint8_t" Function,-,scalbln,double,"double, long int" Function,-,scalblnf,float,"float, long int" Function,-,scalblnl,long double,"long double, long"