From 7a66801ead05c84172a61081e2f4f13ca72f9319 Mon Sep 17 00:00:00 2001 From: librelad Date: Tue, 26 May 2026 20:47:54 +0100 Subject: [PATCH] feat(lazy-load): function manifest generator + lpRegen wiring (Phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/source/files/generate_function_manifest.sh — scans every .sh in scripts/ (skip-list matches generate_arrays.sh, plus excludes peer_shell.sh which is a standalone forced-command target) and emits scripts/source/files/arrays/function_manifest.sh: declare -gA LP_FN_MAP=( [acquireSingletonLock]="crontab/task/crontab_task_processor.sh" [adoptDockerSubnet]="checks/requirements/check_docker_network.sh" ... # 698 entries ) LP_EAGER_FILES=( "backup/db/backup_db.sh" "source/files/arrays/files_app.sh" ... # 32 entries (~7% of files) ) The lazy loader (Phase 3) consumes LP_FN_MAP to install autoload stubs of the form `name() { source "$LP_FN_MAP[name]"; name "$@"; }`. First call sources the real file, which redefines the stub with the real body; subsequent calls hit the real one. LP_EAGER_FILES enumerates files with top-level side effects (variable assignments, source calls, bare commands outside any function) — those MUST always source so the side effects fire. Heuristic correctness, in order of importance: - Function header detection requires EMPTY parens (`name()`), not just `name(` — otherwise lines like `if (...)`, `for (...)`, `while (...)` in embedded awk/perl get misread as bash function defs. - Handles three function styles: `name() {` (same line), `name()\n{` (LibrePortal convention), and one-liners `name() { body; }`. - Tracks { } balance for inside-function depth, with the safe fallback that ambiguous cases get marked eager (false positive = no behaviour change; false negative would skip a needed source). - Files containing embedded awk/perl with their own { } blocks (about 6 of them: cli_debug_commands.sh, crontab_task_processor.sh, backup_db.sh, backup_files.sh, etc.) get false-positive flagged eager — acceptable because they just stay eager-loaded, matching today's behaviour. - Collisions report to stderr (last-write wins, same as eager-load semantics); no collisions found in the current tree. Wiring: - lpRegenArrays (`libreportal regen arrays`) now also runs the manifest generator when the existing arrays need regen, keeping the two in sync. - update.sh's quick-deploy regen step does the same after copying files to the live install. Best-effort: failures don't abort because lazy loading is opt-in (LP_LAZY=1) in Phase 3 and not the default yet. Scanned: 454 files, indexed 698 function definitions, 32 eager (9 real, 23 auto-generated arrays + the manifest itself). 0 name collisions. No behaviour change in this commit — the manifest is just data the loader in Phase 3 will use. The default eager loading path is untouched. Signed-off-by: librelad --- .../source/files/arrays/function_manifest.sh | 749 ++++++++++++++++++ .../files/generate_function_manifest.sh | 230 ++++++ scripts/webui/webui_regen.sh | 11 +- 3 files changed, 988 insertions(+), 2 deletions(-) create mode 100644 scripts/source/files/arrays/function_manifest.sh create mode 100644 scripts/source/files/generate_function_manifest.sh diff --git a/scripts/source/files/arrays/function_manifest.sh b/scripts/source/files/arrays/function_manifest.sh new file mode 100644 index 0000000..ad98b29 --- /dev/null +++ b/scripts/source/files/arrays/function_manifest.sh @@ -0,0 +1,749 @@ +#!/bin/bash + +# This file is auto-generated by generate_function_manifest.sh +# Do not edit manually — run +# ./scripts/source/files/generate_function_manifest.sh run + +# Function name → relative path. Used by the lazy loader (LP_LAZY=1) +# to install an autoload stub for each public function. First call to a +# stub sources the real file, which redefines the function with the real +# body; subsequent calls hit the real one directly. +declare -gA LP_FN_MAP=( + [acquireSingletonLock]="crontab/task/crontab_task_processor.sh" + [adoptDockerSubnet]="checks/requirements/check_docker_network.sh" + [appGenerate]="app/app_generate.sh" + [appGetKeyData]="app/app_get_key_data.sh" + [appInstallCheckRequirements]="checks/requirements/check_app_install.sh" + [appInstallMenu]="menu/menu_app_install.sh" + [_appReqHasDomain]="checks/requirements/check_app_install.sh" + [_appReqServiceInstalled]="checks/requirements/check_app_install.sh" + [_appReqServiceMsg]="checks/requirements/check_app_install.sh" + [appScanAvailable]="app/app_scan_available.sh" + [appStatus]="app/app_status.sh" + [appUninstallMenu]="menu/menu_app_uninstall.sh" + [appUpdateSpecifics]="app/app_update_specifics.sh" + [atomicWriteWebUI]="webui/data/utils/webui_atomic_write.sh" + [authAdapterCall]="app/auth_adapter.sh" + [authAdapterCanDo]="app/auth_adapter.sh" + [authPersistCfg]="app/auth_adapter.sh" + [authToolArg]="app/auth_adapter.sh" + [backupAllApps]="backup/app/backup_app_all.sh" + [backupAppDeleteAll]="backup/app/backup_app_delete.sh" + [backupAppDeleteSnapshot]="backup/app/backup_app_delete.sh" + [backupAppIsLiveSafe]="backup/db/backup_db.sh" + [backupAppLiveCapable]="backup/db/backup_db.sh" + [backupAppRunHook]="backup/app/backup_app_hooks.sh" + [backupAppSchedule]="backup/app/backup_app_schedule.sh" + [backupAppStart]="backup/app/backup_app_start.sh" + [backupAppStrategyOptions]="backup/db/backup_db.sh" + [backupContainerFilesRestore]="function/file/container/restore_files.sh" + [backupContainerFilesToTemp]="function/file/container/backup_files.sh" + [backupDbDescriptors]="backup/db/backup_db.sh" + [backupDbDump]="backup/db/backup_db.sh" + [_backupDbDumpName]="backup/db/backup_db.sh" + [backupDbExcludePaths]="backup/db/backup_db.sh" + [backupDbHasDescriptors]="backup/db/backup_db.sh" + [_backupDbImport]="backup/db/backup_db.sh" + [_backupDbWaitReady]="backup/db/backup_db.sh" + [backupFilesCapture]="backup/files/backup_files.sh" + [backupFilesDescriptors]="backup/files/backup_files.sh" + [backupFilesExcludePaths]="backup/files/backup_files.sh" + [backupFilesHasDescriptors]="backup/files/backup_files.sh" + [backupLocationConfig]="backup/locations/location_paths.sh" + [backupLocationDir]="backup/locations/location_paths.sh" + [backupLocationEnsureDir]="backup/locations/location_paths.sh" + [backupLocationKopiaConfig]="backup/locations/location_paths.sh" + [backupLocationLocalGuard]="backup/locations/location_paths.sh" + [backupLocationOwner]="backup/locations/location_paths.sh" + [backupLocationResolvedPath]="backup/locations/location_paths.sh" + [backupLocationsDir]="backup/locations/location_paths.sh" + [backupLocationsMigrate]="backup/locations/location_migrate.sh" + [backupLocationSshKey]="backup/locations/location_paths.sh" + [backupResolveStrategy]="backup/db/backup_db.sh" + [backupRestoreSystemConfig]="backup/system/backup_system.sh" + [backupSchedule]="backup/app/backup_app_start.sh" + [backupScheduleEnabledApps]="backup/app/backup_schedule_all.sh" + [backupSshCommand]="backup/engine/backup_ssh.sh" + [backupSshKeyDelete]="backup/locations/location_ssh.sh" + [backupSshKeyExists]="backup/locations/location_ssh.sh" + [backupSshKeyFile]="backup/locations/location_ssh.sh" + [backupSshKeyGenerate]="backup/locations/location_ssh.sh" + [backupSshKeyPublic]="backup/locations/location_ssh.sh" + [backupSshKeyRefreshUi]="backup/locations/location_ssh.sh" + [backupSshKeySet]="backup/locations/location_ssh.sh" + [backupSystemConfig]="backup/system/backup_system.sh" + [backupVerifySnapshot]="backup/verify/backup_verify.sh" + [borgArchiveName]="backup/engine/borg_env.sh" + [borgBackupAppToLocation]="backup/engine/borg_backup.sh" + [borgBackupSystemToLocation]="backup/engine/borg_backup.sh" + [borgCheckLocation]="backup/engine/borg_check.sh" + [borgDumpFile]="backup/engine/borg_restore.sh" + [borgEnsureLocationReady]="backup/engine/borg_init.sh" + [borgEnvExport]="backup/engine/borg_env.sh" + [borgEnvUnset]="backup/engine/borg_env.sh" + [borgForgetApp]="backup/engine/borg_forget.sh" + [borgForgetSystem]="backup/engine/borg_forget.sh" + [borgInitLocation]="backup/engine/borg_init.sh" + [borgInstall]="backup/engine/borg_install.sh" + [borgLocationStats]="backup/engine/borg_check.sh" + [borgLocationUri]="backup/engine/borg_env.sh" + [borgRestoreSnapshot]="backup/engine/borg_restore.sh" + [borgRestoreSystemLatest]="backup/engine/borg_restore.sh" + [borgSnapshotsJson]="backup/engine/borg_snapshots.sh" + [changeRootOwnedFile]="function/permission/ownership/root_file.sh" + [changeUserGroupOnFolder]="function/permission/ownership/folder_group.sh" + [checkApplicationsConfigFilesMissingVariables]="config/application/application_missing_variables.sh" + [checkCommandRequirement]="checks/requirements/check_command.sh" + [checkConfigFilesMissingFiles]="config/core/config_check_missing.sh" + [checkConfigFilesMissingVariables]="config/core/variables/config_scan_variables.sh" + [checkConfigFirstInstall]="checks/first_install.sh" + [checkConfigRequirement]="checks/requirements/check_config.sh" + [checkCrontabRequirement]="checks/requirements/check_crontab.sh" + [checkDatabaseRequirement]="checks/requirements/check_database.sh" + [checkDockerComposeRequirement]="checks/requirements/check_docker_compose.sh" + [checkDockerNetworkRequirement]="checks/requirements/check_docker_network.sh" + [checkDockerRequirement]="checks/requirements/check_docker.sh" + [checkDockerRootlessRequirement]="checks/requirements/check_docker_rootless.sh" + [checkDockerSwitcherRequirement]="checks/requirements/check_docker_switcher.sh" + [checkIfOSUpdateShouldRun]="database/check_os_update.sh" + [checkInstallTypeRequirement]="checks/requirements/check_install_type.sh" + [checkLibrePortalConfigFilesMissingVariables]="config/core/variables/config_missing_variables.sh" + [checkLibrePortalWebUIAppRequirement]="checks/requirements/check_webui_app.sh" + [checkLibrePortalWebUIImageRequirement]="checks/requirements/check_webui_image.sh" + [checkPasswordsRequirement]="checks/requirements/check_passwords.sh" + [checkRequirements]="checks/check_requirements.sh" + [checkRootRequirement]="checks/requirements/check_root.sh" + [checkSSLCertsRequirement]="checks/requirements/check_sslcerts.sh" + [checkSuccess]="function/checks/check_success.sh" + [checkSuggestInstallsRequirement]="checks/requirements/check_suggest_installs.sh" + [checkSwapfileRequirement]="checks/requirements/check_swapfile.sh" + [check_task_processor_health]="crontab/task/crontab_check_processor.sh" + [checkTraefikRequirement]="checks/requirements/check_traefik.sh" + [checkUFWDRequirement]="checks/requirements/check_ufwd.sh" + [checkUFWRequirement]="checks/requirements/check_ufw.sh" + [checkUpdates]="update/check_update.sh" + [checkWebUISystemdRequirement]="checks/requirements/check_webui_systemd.sh" + [cleanupZeroByteFiles]="crontab/task/crontab_task_processor.sh" + [cliAppRestore]="cli/commands/app/cli_app_restore.sh" + [cliAppToolList]="cli/commands/app/cli_app_tool_list.sh" + [cliDebugLoadTrace]="cli/commands/debug/cli_debug_commands.sh" + [cliFirewallHeader]="cli/commands/firewall/cli_firewall_header.sh" + [cliHandleAppCommands]="cli/commands/app/cli_app_commands.sh" + [cliHandleBackupCommands]="cli/commands/backup/cli_backup_commands.sh" + [cliHandleConfigCommands]="cli/commands/config/cli_config_commands.sh" + [cliHandleDebugCommands]="cli/commands/debug/cli_debug_commands.sh" + [cliHandleDockertypeCommands]="cli/commands/dockertype/cli_dockertype_commands.sh" + [cliHandleFirewallCommands]="cli/commands/firewall/cli_firewall_commands.sh" + [cliHandleHelpCommands]="cli/commands/help/cli_help_commands.sh" + [cliHandleInstallCommands]="cli/commands/install/cli_install_commands.sh" + [cliHandleIPCommands]="cli/commands/ip/cli_ip_commands.sh" + [cliHandlePeerCommands]="cli/commands/peer/cli_peer_commands.sh" + [cliHandleRegenCommands]="cli/commands/regen/cli_regen_commands.sh" + [cliHandleResetCommands]="cli/commands/reset/cli_reset_commands.sh" + [cliHandleRestoreCommands]="cli/commands/restore/cli_restore_commands.sh" + [cliHandleSetupCommands]="cli/commands/setup/cli_setup_commands.sh" + [cliHandleSshCommands]="cli/commands/ssh/cli_ssh_commands.sh" + [cliHandleSystemCommands]="cli/commands/system/cli_system_commands.sh" + [cliHandleUpdateCommands]="cli/commands/update/cli_update_commands.sh" + [cliHandleValidationCommands]="cli/commands/validation/cli_validation_commands.sh" + [cliHandleWebuiCommands]="cli/commands/webui/cli_webui_commands.sh" + [cliInitialize]="cli/cli_initialize.sh" + [cliShowAppHelp]="cli/commands/app/cli_app_header.sh" + [cliShowBackupHelp]="cli/commands/backup/cli_backup_header.sh" + [cliShowConfigHelp]="cli/commands/config/cli_config_header.sh" + [cliShowDebugHelp]="cli/commands/debug/cli_debug_header.sh" + [cliShowDockertypeHelp]="cli/commands/dockertype/cli_dockertype_header.sh" + [cliShowHelpHelp]="cli/commands/help/cli_help_header.sh" + [cliShowInstallHelp]="cli/commands/install/cli_install_header.sh" + [cliShowIPHelp]="cli/commands/ip/cli_ip_header.sh" + [cliShowPeerHelp]="cli/commands/peer/cli_peer_header.sh" + [cliShowRegenHelp]="cli/commands/regen/cli_regen_header.sh" + [cliShowResetHelp]="cli/commands/reset/cli_reset_header.sh" + [cliShowRestoreHelp]="cli/commands/restore/cli_restore_header.sh" + [cliShowSetupHelp]="cli/commands/setup/cli_setup_header.sh" + [cliShowSshHelp]="cli/commands/ssh/cli_ssh_header.sh" + [cliShowSystemHelp]="cli/commands/system/cli_system_header.sh" + [cliShowUpdateHelp]="cli/commands/update/cli_update_header.sh" + [cliShowValidationHelp]="cli/commands/validation/cli_validation_header.sh" + [cliShowWebuiHelp]="cli/commands/webui/cli_webui_header.sh" + [cliUpdateCommands]="cli/cli_update.sh" + [cliWebuiLoginReset]="cli/commands/webui/cli_webui_commands.sh" + [completeMessage]="menu/message/complete.sh" + [configSetupFileWithData]="config/core/config_file_setup_data.sh" + [configUpdateBatch]="config/config_update.sh" + [containsElement]="function/validation/email.sh" + [copyFile]="function/file/copy_file.sh" + [copyFiles]="function/file/copy_files.sh" + [copyFolder]="function/folder/copy_folder.sh" + [copyFolders]="function/folder/copy_folders.sh" + [copyResource]="function/file/copy_resource.sh" + [createFolders]="function/folder/create_folder.sh" + [createSuccessfulRunFile]="function/run/create_successful_run_file.sh" + [createTaskFile]="webui/data/generators/backup/webui_task_create.sh" + [createTouch]="function/file/create_touch.sh" + [crontabClean]="crontab/crontab_clean.sh" + [crontabClear]="crontab/crontab_clear.sh" + [crontabRefresh]="crontab/crontab_refresh.sh" + [crontabSetup]="crontab/crontab_setup.sh" + [crontabSetupBackupScheduler]="crontab/app/crontab_backup_scheduler.sh" + [crontabSetupCheckProcessor]="crontab/task/crontab_setup_check_processor.sh" + [crontabSetupSystemInfoUpdater]="crontab/system/crontab_setup_system_info_updater.sh" + [crontabSetupTaskProcessor]="crontab/task/crontab_setup_task_processor.sh" + [crontabToolsMenu]="menu/tools/manage_crontab.sh" + [dashyToolsMenu]="menu/tools/manage_dashy.sh" + [databaseAppScan]="database/app/db_app_scan.sh" + [databaseBackupInsert]="database/insert/db_insert_backups.sh" + [databaseCreateTables]="database/tables/db_create_tables.sh" + [databaseCycleThroughListApps]="database/app/db_cycle_apps.sh" + [databaseDisplayTables]="database/tables/db_display_tables.sh" + [databaseEmptyTable]="database/tables/db_empty_table.sh" + [databaseInstallApp]="database/app/db_install_app.sh" + [databaseListAllApps]="database/app/db_list_all_apps.sh" + [databaseListInstalledApp]="database/app/db_list_installed_app.sh" + [databaseListInstalledApps]="database/app/db_list_installed_apps.sh" + [databaseOptionInsert]="database/insert/db_insert_option.sh" + [databasePortOpenInsert]="database/insert/db_insert_port_open.sh" + [databasePortUsedInsert]="database/insert/db_insert_port_used.sh" + [databaseRemoveFile]="database/delete_db_file.sh" + [databaseRestoreInsert]="database/insert/db_insert_restore.sh" + [databaseUninstallApp]="database/app/db_uninstall_app.sh" + [detectOS]="function/checks/detect_os.sh" + [dispatchPending]="crontab/task/crontab_task_processor.sh" + [dispatchSpecific]="crontab/task/crontab_task_processor.sh" + [dockerAppRunTool]="docker/app/functions/function_app_tool.sh" + [dockerCheckAppHealthDetails]="docker/checks/app_health_details.sh" + [dockerCheckAppHealthStatus]="docker/checks/app_health_status.sh" + [dockerCheckAppInstalled]="docker/app/checks/app_installed.sh" + [dockerCheckContainerHealth]="docker/app/checks/container_health.sh" + [dockerCheckContainerHealthLoop]="docker/app/checks/container_health_loop.sh" + [dockerCheckIsRunningForUser]="docker/checks/running_for_user.sh" + [dockerCommandRun]="docker/command/docker_run.sh" + [dockerCommandRunInstallUser]="docker/command/docker_run_install.sh" + [dockerComposeDown]="docker/app/compose/down_app.sh" + [dockerComposeDownAllApps]="docker/app/compose/down_all.sh" + [dockerComposeDownRemove]="docker/app/uninstall/down_remove_app.sh" + [dockerComposeRestart]="docker/app/compose/up_down_app.sh" + [dockerComposeRestartAfterUpdate]="docker/compose/restart_after_update.sh" + [dockerComposeSetupFile]="docker/compose/setup_compose_yml.sh" + [dockerComposeUp]="docker/app/compose/up_app.sh" + [dockerComposeUpAllApps]="docker/app/compose/up_all.sh" + [dockerComposeUpdate]="docker/compose/update_compose_yml.sh" + [dockerComposeUpdateAndStartApp]="docker/compose/update_and_start.sh" + [dockerConfigSetupFileWithData]="config/docker/docker_config_setup_data.sh" + [dockerConfigSetupToContainer]="config/docker/docker_config_to_container.sh" + [dockerContainerOwner]="function/permission/libreportal_folders.sh" + [dockerCopyBuildContext]="docker/compose/copy_build_context.sh" + [dockerDeleteData]="docker/app/uninstall/delete_data.sh" + [dockerInstallApp]="docker/app/functions/function_install_app.sh" + [dockerPruneAppNetworks]="docker/network/network_prune.sh" + [dockerRemoveApp]="docker/app/docker/remove_app.sh" + [dockerRemoveAppImages]="docker/app/uninstall/remove_images.sh" + [dockerRestartApp]="docker/app/docker/restart_app.sh" + [dockerRestartAppViaInstall]="docker/app/functions/function_restart_app.sh" + [dockerServiceStart]="docker/service/start_docker.sh" + [dockerServiceStop]="docker/service/stop_docker.sh" + [dockerSetupEnvFile]="docker/setup_env.sh" + [dockerStartAllApps]="docker/app/docker/start_all.sh" + [dockerStartApp]="docker/app/docker/start_app.sh" + [dockerStopAllApps]="docker/app/docker/stop_all.sh" + [dockerStopApp]="docker/app/docker/stop_app.sh" + [dockerSwitcherScanContainersForSocket]="docker/type_switcher/scan_container_socket.sh" + [dockerSwitcherSetSocketPermissions]="docker/type_switcher/set_socket_permissions.sh" + [dockerSwitcherSwap]="docker/type_switcher/swap_docker_type.sh" + [dockerSwitcherUpdateContainersToDockerType]="docker/type_switcher/switch_containers_type.sh" + [dockerToolsMenu]="menu/tools/manage_docker.sh" + [dockerUninstallApp]="docker/app/uninstall/uninstall_app.sh" + [editAppConfig]="config/application/application_edit_config.sh" + [emailValidation]="function/validation/element.sh" + [endStart]="start/start_end.sh" + [engineBackupApp]="backup/engine/engine_dispatch.sh" + [engineBackupSystem]="backup/engine/engine_dispatch.sh" + [engineCheckAllLocations]="backup/engine/engine_dispatch.sh" + [engineCheckLocation]="backup/engine/engine_dispatch.sh" + [engineDispatch]="backup/engine/engine_dispatch.sh" + [engineDumpFile]="backup/engine/engine_dispatch.sh" + [engineEnsureAllLocationsReady]="backup/engine/engine_dispatch.sh" + [engineEnsureLocationReady]="backup/engine/engine_dispatch.sh" + [engineEnvExport]="backup/engine/engine_dispatch.sh" + [engineEnvUnset]="backup/engine/engine_dispatch.sh" + [engineForgetApp]="backup/engine/engine_dispatch.sh" + [engineForgetAppAllLocations]="backup/engine/engine_dispatch.sh" + [engineForgetSystem]="backup/engine/engine_dispatch.sh" + [engineForLocation]="backup/engine/engine_dispatch.sh" + [engineInitAllLocations]="backup/engine/engine_dispatch.sh" + [engineInitLocation]="backup/engine/engine_dispatch.sh" + [engineInstallAll]="backup/engine/engine_dispatch.sh" + [engineKnownIds]="backup/engine/engine_dispatch.sh" + [engineLocationStats]="backup/engine/engine_dispatch.sh" + [engineLocationUri]="backup/engine/engine_dispatch.sh" + [enginePasswordEnsure]="backup/engine/engine_dispatch.sh" + [engineRestoreSnapshot]="backup/engine/engine_dispatch.sh" + [engineRestoreSystemLatest]="backup/engine/engine_dispatch.sh" + [engineSnapshotLatestId]="backup/engine/engine_dispatch.sh" + [engineSnapshotListFiles]="backup/engine/engine_dispatch.sh" + [engineSnapshotsJson]="backup/engine/engine_dispatch.sh" + [engineSystemSnapshotsJson]="backup/engine/engine_dispatch.sh" + [exitScript]="start/start_exit.sh" + [exportBcryptPassword]="config/password/bcrypt/password_export_bcrypt.sh" + [fileHasEmptyLine]="function/file/empty_line/check_empty.sh" + [findConfigFileForOption]="config/core/config_find_file.sh" + [firewallClearLibrePortalRules]="network/firewall/rules/firewall_clear_rules.sh" + [firewallInitialSetup]="network/firewall/firewall_initial_setup.sh" + [firewallRebuildFromDatabase]="network/firewall/rules/firewall_rebuild_from_db.sh" + [firewallRefreshAll]="network/firewall/rules/firewall_refresh_all.sh" + [fixAppFolderPermissions]="function/permission/app_folder.sh" + [fixConfigPermissions]="function/permission/config.sh" + [fixFolderPermissions]="function/permission/libreportal_folders.sh" + [fixPermissionsBeforeStart]="function/permission/before_start.sh" + [generateHealthReport]="crontab/task/crontab_check_processor.sh" + [generateInstallName]="checks/generate_install_name.sh" + [generateRandomPassword]="config/password/password_generate.sh" + [generateRandomUsername]="config/password/password_user_generator.sh" + [getConfigOptionData]="config/core/config_get_config_data.sh" + [getLibrePortalWebUIUrls]="webui/webui_display_logins.sh" + [getStoredPassword]="config/password/bcrypt/password_retreive_bcrypt.sh" + [gitCheckConfigs]="update/git/checks/config_git_check.sh" + [gitCheckForUpdate]="update/git/checks/update_git_check.sh" + [gitCheckGitDetails]="update/git/check_git_details.sh" + [gitCleanInstallBackups]="update/backup/install_git_backup.sh" + [gitFolderResetAndBackup]="update/backup/reset_git_backup.sh" + [gitPerformUpdate]="update/backup/reset_git_backup.sh" + [gitReset]="update/git/reset_git.sh" + [gitUntrackFiles]="update/git/untrack_files.sh" + [gitUseExistingBackup]="update/backup/use_git_backup.sh" + [hashPassword]="config/password/password_hash.sh" + [healthLogError]="crontab/task/crontab_check_processor.sh" + [healthLogInfo]="crontab/task/crontab_check_processor.sh" + [healthLogSuccess]="crontab/task/crontab_check_processor.sh" + [healthLogWarning]="crontab/task/crontab_check_processor.sh" + [hostAppInstall]="install/host_app.sh" + [hostSshAuthKeysFile]="ssh/host_access.sh" + [hostSshEnsureDir]="ssh/host_access.sh" + [hostSshHome]="ssh/host_access.sh" + [hostSshKeyAdd]="ssh/host_access.sh" + [hostSshKeyCount]="ssh/host_access.sh" + [hostSshKeyRemove]="ssh/host_access.sh" + [hostSshPasswordAuthEnabled]="ssh/host_access.sh" + [hostSshRefreshUi]="ssh/host_access.sh" + [hostSshSetPasswordAuth]="ssh/host_access.sh" + [hostSshUser]="ssh/host_access.sh" + [initializeAppVariables]="network/variables/variables_init_app.sh" + [installArch]="os/install/arch.sh" + [installCrontab]="crontab/crontab_install.sh" + [installDebianUbuntu]="os/install/ubuntu.sh" + [installDockerNetwork]="docker/network/network_setup.sh" + [installDockerRooted]="docker/install/rooted/rooted_docker.sh" + [installDockerRootedCheck]="docker/install/rooted/rooted_docker_check.sh" + [installDockerRootedCompose]="docker/install/rooted/rooted_docker_compose.sh" + [installDockerRootless]="docker/install/rootless/rootless_docker.sh" + [installDockerRootlessStartSetup]="docker/install/rootless/rootless_start_setup.sh" + [installDockerRootlessUser]="docker/install/rootless/rootless_user.sh" + [installLibrePortalAppWebUI]="webui/webui_install_app.sh" + [installLibrePortalImageWebUI]="webui/webui_install_image.sh" + [installLibrePortalWebUITaskService]="webui/webui_install_systemd.sh" + [installOptionalMetricsApps]="start/start_recommended.sh" + [installRecommendedApps]="start/start_recommended.sh" + [installResticHost]="install/install_restic.sh" + [installResticMigrateLegacyPasswords]="install/install_restic.sh" + [installSQLiteDatabase]="database/install_sqlite.sh" + [installSSLCertificate]="install/install_certificate.sh" + [installSwapfile]="install/install_swapfile.sh" + [installUFW]="install/install_ufw.sh" + [installUFWDocker]="install/install_ufwd.sh" + [invidiousToolsMenu]="menu/tools/manage_invidious.sh" + [ipAllocation]="network/ip/ip_allocation.sh" + [ipFindAvailable]="network/ip/ip_find_available.sh" + [ipIsAvailable]="network/ip/ip_is_available.sh" + [ipRemoveFromDatabase]="network/ip/ip_remove_from_db.sh" + [ip_scan_all_network_services]="network/display/show_all_network_services_detailed.sh" + [ip_scan_network_conflicts]="network/display/show_network_conflicts.sh" + [ip_scan_network_health]="network/display/show_network_health_detailed.sh" + [ip_scan_traefik_services]="network/display/show_traefik_services.sh" + [ip_show_allocations]="network/display/show_ip_allocations.sh" + [ipUpdateComposeTags]="network/ip/ip_replace_tags.sh" + [isError]="menu/message/markers.sh" + [isFatalError]="menu/message/markers.sh" + [isFatalErrorExit]="menu/message/markers.sh" + [isHeader]="menu/message/markers.sh" + [isNotice]="menu/message/markers.sh" + [isOption]="menu/message/markers.sh" + [isOptionMenu]="menu/message/markers.sh" + [isQuestion]="menu/message/markers.sh" + [isSetupWizardComplete]="setup/setup_lock.sh" + [isSuccessful]="menu/message/markers.sh" + [kopiaBackupAppToLocation]="backup/engine/kopia_backup.sh" + [kopiaBackupSystemToLocation]="backup/engine/kopia_backup.sh" + [kopiaCheckLocation]="backup/engine/kopia_check.sh" + [kopiaConfigPath]="backup/engine/kopia_env.sh" + [kopiaDumpFile]="backup/engine/kopia_restore.sh" + [kopiaEnsureLocationReady]="backup/engine/kopia_init.sh" + [kopiaEnvExport]="backup/engine/kopia_env.sh" + [kopiaEnvUnset]="backup/engine/kopia_env.sh" + [kopiaForgetApp]="backup/engine/kopia_forget.sh" + [kopiaForgetSystem]="backup/engine/kopia_forget.sh" + [kopiaInitLocation]="backup/engine/kopia_init.sh" + [kopiaInstall]="backup/engine/kopia_install.sh" + [kopiaLocationStats]="backup/engine/kopia_check.sh" + [kopiaLocationUri]="backup/engine/kopia_env.sh" + [kopiaRestoreSnapshot]="backup/engine/kopia_restore.sh" + [kopiaSnapshotsJson]="backup/engine/kopia_snapshots.sh" + [listDockerComposeFiles]="config/docker/docker_list_compose_files.sh" + [localDnsAppHosts]="network/dns/setup_local_dns.sh" + [localDnsApplyAdguard]="network/dns/setup_local_dns.sh" + [localDnsApplyPihole]="network/dns/setup_local_dns.sh" + [localDnsDomains]="network/dns/setup_local_dns.sh" + [localDnsServerIp]="network/dns/setup_local_dns.sh" + [locationAdd]="backup/locations/location_add.sh" + [locationRemove]="backup/locations/location_remove.sh" + [logDebug]="crontab/task/crontab_task_processor.sh" + [logError]="crontab/task/crontab_task_processor.sh" + [logInfo]="crontab/task/crontab_task_processor.sh" + [_lpDownload]="source/fetch.sh" + [lpFetchRelease]="source/fetch.sh" + [lpFetchSource]="source/fetch.sh" + [_lpFetchTool]="source/fetch.sh" + [lpInstalledFootprintVersion]="source/fetch.sh" + [_lpJsonNum]="source/fetch.sh" + [_lpJsonStr]="source/fetch.sh" + [lpRegen]="webui/webui_regen.sh" + [lpRegenArrays]="webui/webui_regen.sh" + [_lpRegenStale]="webui/webui_regen.sh" + [lpRegenWebui]="webui/webui_regen.sh" + [lpReleaseBaseUrl]="source/fetch.sh" + [lpReleaseChannel]="source/fetch.sh" + [lpReleaseLatestFootprint]="source/fetch.sh" + [lpReleaseLatestVersion]="source/fetch.sh" + [_lpSha256]="source/fetch.sh" + [lpVersionGt]="source/fetch.sh" + [mainLoop]="crontab/task/crontab_task_processor.sh" + [mainMenu]="menu/menu_main.sh" + [manifestCollect]="backup/manifest/manifest_collect.sh" + [manifestReadField]="backup/manifest/manifest_read.sh" + [manifestReadFromSnapshot]="backup/manifest/manifest_read.sh" + [manifestRemove]="backup/manifest/manifest_write.sh" + [manifestWrite]="backup/manifest/manifest_write.sh" + [mattermostToolsMenu]="menu/tools/manage_mattermost.sh" + [maybeRegenPoll]="crontab/task/crontab_task_processor.sh" + [menuContinue]="menu/message/continue.sh" + [menuLoginRequired]="menu/message/login.sh" + [menuShowFinalMessages]="menu/message/final.sh" + [_metricsReadCpu]="webui/data/generators/system/webui_system_metrics.sh" + [migrateApp]="migrate/migrate_apply.sh" + [migrateApplyApp]="migrate/migrate_apply.sh" + [migrateApplySystem]="migrate/migrate_apply.sh" + [migrateApplyUrlRewrite]="migrate/migrate_url_rewrite.sh" + [migrateDiscoverAppDetail]="migrate/migrate_discover.sh" + [migrateDiscoverApps]="migrate/migrate_discover.sh" + [migrateDiscoverAppsForHost]="migrate/migrate_discover.sh" + [migrateDiscoverHosts]="migrate/migrate_discover.sh" + [migrateEmit]="migrate/migrate_progress.sh" + [_migrateParseOpts]="migrate/migrate_apply.sh" + [migratePreBackupDestination]="migrate/migrate_pre_backup.sh" + [migratePreflight]="migrate/migrate_preflight.sh" + [_migratePreflightAppend]="migrate/migrate_preflight.sh" + [_migrateResolveLocation]="migrate/migrate_discover.sh" + [migrateRunHook]="migrate/migrate_hooks.sh" + [migrateSystem]="migrate/migrate_apply.sh" + [migrateUrlRewriteEnabled]="migrate/migrate_url_rewrite.sh" + [monitoringAppEnabled]="network/monitoring/monitoring.sh" + [monitoringInstalledApps]="network/monitoring/monitoring.sh" + [monitoringIsInstalled]="network/monitoring/monitoring.sh" + [monitoringToggleAppConfig]="network/monitoring/monitoring.sh" + [moveFile]="function/file/move_file.sh" + [openFifoReader]="crontab/task/crontab_task_processor.sh" + [passwordValidation]="function/validation/password.sh" + [peerAdd]="peer/peer_add.sh" + [peerCheckAll]="peer/peer_check.sh" + [peerCheckReachable]="peer/peer_check.sh" + [_peerDb]="peer/peer_helpers.sh" + [_peerEnvPath]="peer/peer_install_shell.sh" + [peerExec]="peer/peer_remote.sh" + [peerGet]="peer/peer_list.sh" + [peerInstallShell]="peer/peer_install_shell.sh" + [_peerKeyDir]="peer/peer_key.sh" + [peerKeyEnsure]="peer/peer_key.sh" + [peerKeyFingerprint]="peer/peer_key.sh" + [_peerKeyPrivPath]="peer/peer_key.sh" + [peerKeyPublic]="peer/peer_key.sh" + [_peerKeyPubPath]="peer/peer_key.sh" + [peerList]="peer/peer_list.sh" + [peerListAppsRemote]="peer/peer_remote.sh" + [peerNameForHostname]="peer/peer_list.sh" + [peerPairingAccept]="peer/peer_pairing.sh" + [_peerPairingJsonNum]="peer/peer_pairing.sh" + [_peerPairingJsonStr]="peer/peer_pairing.sh" + [peerPairingParse]="peer/peer_pairing.sh" + [peerPairingToken]="peer/peer_pairing.sh" + [peerPing]="peer/peer_remote.sh" + [peerPullApp]="peer/peer_pull.sh" + [peerRemove]="peer/peer_remove.sh" + [_peerShellPath]="peer/peer_install_shell.sh" + [_peerShellSrc]="peer/peer_install_shell.sh" + [peerSqlEscape]="peer/peer_helpers.sh" + [_peerSshOpts]="peer/peer_remote.sh" + [_peerSshTarget]="peer/peer_remote.sh" + [peerValidateKind]="peer/peer_helpers.sh" + [peerValidateName]="peer/peer_helpers.sh" + [performMaintenance]="crontab/task/crontab_check_processor.sh" + [portAllocate]="network/ports/allocation/port_allocate.sh" + [portFindNextAvailablePort]="network/ports/core/port_find_next_available.sh" + [portGetPublicPorts]="network/ports/core/port_get_public_ports.sh" + [portGetServicePorts]="network/ports/core/port_get_service_ports.sh" + [portGetServicePortsOnly]="network/ports/core/port_get_service_ports_only.sh" + [portIsReservedHostPort]="network/ports/core/port_find_next_available.sh" + [portLookupExisting]="network/ports/allocation/port_allocate.sh" + [port_show_all_network_services]="network/display/show_all_network_services.sh" + [port_show_network_service]="network/display/show_network_service.sh" + [port_show_network_services_by_app]="network/display/show_network_services_by_app.sh" + [port_show_network_services_by_category]="network/display/show_network_services_by_category.sh" + [port_show_network_statistics]="network/display/show_network_statistics.sh" + [portsRemoveFromDatabase]="network/ports/core/port_remove_from_db.sh" + [portStoreMapping]="network/ports/allocation/port_store_mapping.sh" + [portUpdateComposeTags]="network/ports/allocation/port_update_compose_tags.sh" + [processBcryptPassword]="config/password/bcrypt/password_process_bcrypt.sh" + [readTaskField]="crontab/task/crontab_task_processor.sh" + [reconcileConfigFile]="config/core/variables/config_scan_variables.sh" + [reconcileContainersTopOwnership]="function/permission/libreportal_folders.sh" + [reconcileDockerOwnership]="function/permission/libreportal_folders.sh" + [reconcileWebuiDirOwnership]="function/permission/libreportal_folders.sh" + [recoverOrphans]="crontab/task/crontab_task_processor.sh" + [removeEmptyLineAtFileEnd]="function/file/empty_line/remove_line.sh" + [repairDirectoryStructure]="crontab/task/crontab_check_processor.sh" + [repairFileSystem]="crontab/task/crontab_check_processor.sh" + [repairPermissions]="crontab/task/crontab_check_processor.sh" + [repairSystemIssues]="crontab/task/crontab_check_processor.sh" + [repairSystemService]="crontab/task/crontab_check_processor.sh" + [repairTaskSystem]="crontab/task/crontab_check_processor.sh" + [replaceBcryptPasswords]="config/password/bcrypt/password_replace_bcrypt.sh" + [replaceHexKeys]="config/password/password_replace hex.sh" + [replaceLaravelAppKeys]="config/password/password_replace_appkey.sh" + [replacePlainPasswords]="config/password/password_replace.sh" + [replaceRandomUsernames]="config/password/password_user_replace.sh" + [replaceVAPIDKeys]="config/password/password_replace vapid.sh" + [resetToMenu]="menu/menu_reset_to_menu.sh" + [resolveDockerInstallUser]="checks/requirements/check_install_type.sh" + [resticAllLocationIndices]="backup/engine/restic_env.sh" + [resticBackupAppAllLocations]="backup/engine/restic_backup.sh" + [resticBackupAppToLocation]="backup/engine/restic_backup.sh" + [resticBackupSystemToLocation]="backup/engine/restic_backup.sh" + [resticCheckAllLocations]="backup/engine/restic_check.sh" + [resticCheckLocation]="backup/engine/restic_check.sh" + [resticDumpFile]="backup/engine/restic_dump.sh" + [resticEnabledLocations]="backup/engine/restic_env.sh" + [resticEnsureAllLocationsReady]="backup/engine/restic_init.sh" + [resticEnsureLocationReady]="backup/engine/restic_init.sh" + [resticEnvExport]="backup/engine/restic_env.sh" + [resticEnvUnset]="backup/engine/restic_env.sh" + [resticForgetApp]="backup/engine/restic_forget.sh" + [resticForgetAppAllLocations]="backup/engine/restic_forget.sh" + [resticForgetSystem]="backup/engine/restic_forget.sh" + [resticInitAllLocations]="backup/engine/restic_init.sh" + [resticInitLocation]="backup/engine/restic_init.sh" + [resticInstall]="backup/engine/restic_install.sh" + [resticLocationAppendOnly]="backup/engine/restic_env.sh" + [resticLocationEnabled]="backup/engine/restic_env.sh" + [resticLocationField]="backup/engine/restic_env.sh" + [resticLocationName]="backup/engine/restic_env.sh" + [resticLocationPassword]="backup/engine/restic_env.sh" + [resticLocationStats]="backup/engine/restic_check.sh" + [resticLocationType]="backup/engine/restic_env.sh" + [resticLocationUri]="backup/engine/restic_env.sh" + [resticNextFreeIndex]="backup/engine/restic_env.sh" + [resticRestoreAppLatest]="backup/engine/restic_restore.sh" + [resticRestoreSnapshot]="backup/engine/restic_restore.sh" + [resticRestoreSystemLatest]="backup/engine/restic_restore.sh" + [resticRetentionFor]="backup/engine/restic_forget.sh" + [resticSnapshotLatestId]="backup/engine/restic_snapshots.sh" + [resticSnapshotListFiles]="backup/engine/restic_snapshots.sh" + [resticSnapshotsJson]="backup/engine/restic_snapshots.sh" + [resticSystemSnapshotsJson]="backup/engine/restic_snapshots.sh" + [restoreAppRunHook]="restore/restore_app_hooks.sh" + [restoreAppStart]="restore/restore_app_start.sh" + [restoreDbRehydratePreStart]="backup/db/backup_db.sh" + [restoreDbReplayPostStart]="backup/db/backup_db.sh" + [restoreFilesRehydratePreStart]="backup/files/backup_files.sh" + [restoreFirstRunBulk]="restore/restore_first_run.sh" + [restoreFirstRunDiscover]="restore/restore_first_run.sh" + [restorePickSnapshot]="restore/restore_app_pick.sh" + [runAppCfg]="docker/command/run_privileged.sh" + [runAsManager]="docker/command/run_privileged.sh" + [runBackupOp]="docker/command/run_privileged.sh" + [runBinInstall]="docker/command/run_privileged.sh" + [runFileOp]="docker/command/run_privileged.sh" + [runFileWrite]="docker/command/run_privileged.sh" + [runInstallOp]="docker/command/run_privileged.sh" + [runInstallWrite]="docker/command/run_privileged.sh" + [runOwnership]="docker/command/run_privileged.sh" + [runReinstall]="function/run/reinstall_libreportal.sh" + [runResolv]="docker/command/run_privileged.sh" + [_runRootHelper]="docker/command/run_privileged.sh" + [runSocket]="docker/command/run_privileged.sh" + [runSshAccess]="docker/command/run_privileged.sh" + [runSvc]="docker/command/run_privileged.sh" + [runSystem]="docker/command/run_privileged.sh" + [runTask]="crontab/task/crontab_task_processor.sh" + [run_task_processor]="crontab/task/crontab_task_processor.sh" + [scanConfigsForRandomPassword]="config/password/password_update_all.sh" + [scanFileForRandomPasswordKeysUsers]="config/password/password_scan_file.sh" + [setupApply]="setup/setup_apply.sh" + [setupApplyConfig]="setup/setup_apply.sh" + [setupApplyFinalize]="setup/setup_apply.sh" + [setupBasicScanVariables]="network/variables/basic_scan.sh" + [setupCheckDomainPointsHere]="setup/setup_apply.sh" + [setupDNSIP]="network/dns/setup_dns_ip.sh" + [setupGenerateName]="setup/setup_apply.sh" + [setupHeadscaleVariables]="network/variables/headscale_variables.sh" + [setupTaskDir]="crontab/task/crontab_task_processor.sh" + [setupWizardMarkComplete]="setup/setup_lock.sh" + [setupWizardReset]="setup/setup_lock.sh" + [setupWizardTerminal]="checks/first_install.sh" + [showInstructions]="menu/message/instructions.sh" + [sourceBackupLocations]="backup/locations/location_loader.sh" + [sshRemote]="network/ssh/ssh.sh" + [startInstall]="start/start_install.sh" + [startLoad]="start/start_load.sh" + [startOther]="start/start_other.sh" + [startPreInstall]="start/start_preinstall.sh" + [startScan]="start/start_scan.sh" + [switchMigrateBackupApps]="docker/type_switcher/swap_docker_type.sh" + [switchMigrateRestoreApps]="docker/type_switcher/swap_docker_type.sh" + [tagsManagerGetTagContent]="config/tags/manager/tags_manager_content.sh" + [tagsManagerGetTagState]="config/tags/manager/tags_manager_state.sh" + [tagsManagerUpdateUniversalTag]="config/tags/manager/tags_manager_update.sh" + [tagsProcessorAppConfigValues]="config/tags/processors/tags_processor_app_config_values.sh" + [tagsProcessorAppUrl]="config/tags/processors/tags_processor_app_url.sh" + [tagsProcessorDockerInstallation]="config/tags/processors/tags_processor_docker_installation.sh" + [tagsProcessorHealthcheck]="config/tags/processors/tags_processor_healthcheck.sh" + [tagsProcessorPasswordAndKeyGeneration]="config/tags/processors/tags_processor_password_generation.sh" + [tagsProcessorPortMiddlewares]="network/traefik/traefik_port_middlewares.sh" + [tagsProcessorPortRouterBlocks]="network/traefik/traefik_port_subdomains.sh" + [tagsProcessorPortSubdomains]="network/traefik/traefik_port_subdomains.sh" + [tagsProcessorRandomUserGeneration]="config/tags/processors/tags_processor_random_user.sh" + [tagsProcessorSocketConfiguration]="config/tags/processors/tags_processor_socket_configuration.sh" + [tagsProcessorSpeedtestPass]="config/tags/processors/tags_processor_speedtest_pass.sh" + [tagsProcessorStandardReplacements]="config/tags/processors/tags_processor_standard_replacements.sh" + [tagsProcessorTraefikControl]="config/tags/processors/tags_processor_traefik_control.sh" + [tagsProcessorTrustedDomains]="config/tags/processors/tags_processor_trusted_domains.sh" + [toolArgsGet]="docker/app/functions/function_app_tool.sh" + [toolsMenu]="menu/tools/manage_main.sh" + [traefikSetupLabelsMiddlewares]="network/traefik/traefik_middlewares.sh" + [traefikSetupLoginCredentials]="network/traefik/traefik_login_credentials.sh" + [traefikUpdateWhitelist]="network/traefik/traefik_whitelist.sh" + [uninstallDockerRootless]="docker/install/rootless/rootless_uninstall.sh" + [updateAppConfig]="webui/data/generators/apps/webui_app_config.sh" + [updateConfigOption]="config/core/config_update_option.sh" + [updateDNS]="network/dns/setup_dns.sh" + [updateDockerInstallPassword]="docker/update_docker_user_pass.sh" + [updateDockerNetworkSubnet]="docker/network/network_randomize_subnet.sh" + [updateDockerSudoPassword]="docker/update_docker_sudo_pass.sh" + [updateFileOwnership]="function/permission/ownership/file.sh" + [updateHostIPToWhitelist]="config/utils/update_whitelist.sh" + [updateTaskFields]="crontab/task/crontab_task_processor.sh" + [userExists]="function/checks/user_exists.sh" + [validateContainerHealth]="crontab/task/crontab_check_processor.sh" + [validateDirectoryStructure]="crontab/task/crontab_check_processor.sh" + [validateDiskSpace]="crontab/task/crontab_check_processor.sh" + [validateDockerService]="crontab/task/crontab_check_processor.sh" + [validateFileSystem]="crontab/task/crontab_check_processor.sh" + [validateLibrePortalInstallation]="crontab/task/crontab_check_processor.sh" + [validateLogHealth]="crontab/task/crontab_check_processor.sh" + [validatePermissions]="crontab/task/crontab_check_processor.sh" + [validateSystemHealth]="crontab/task/crontab_check_processor.sh" + [validateSystemService]="crontab/task/crontab_check_processor.sh" + [validateTaskSystem]="crontab/task/crontab_check_processor.sh" + [validateWebUIReadiness]="crontab/task/crontab_check_processor.sh" + [viewAppCategoryConfigs]="config/application/application_menu_category.sh" + [viewAppConfigs]="config/application/application_menu_apps.sh" + [viewComposeFiles]="config/docker/docker_compose_menu.sh" + [viewConfigs]="config/core/config_main_menu.sh" + [viewLibrePortalConfigs]="config/core/config_manage_menu.sh" + [viewLogs]="logs/installed_apps.sh" + [viewLogsAppMenu]="logs/app_log_menu.sh" + [webuiCheckUpdateLock]="webui/data/lock/webui_check_update_lock.sh" + [webuiContainerSetup]="webui/data/utils/webui_container_setup.sh" + [webuiCreateAppFieldMappings]="webui/data/generators/categories/webui_create_app_field_mappings.sh" + [webuiCreateAppsCategories]="webui/data/generators/categories/webui_create_app_categories.sh" + [webuiCreateAppsConfigCategories]="webui/data/generators/categories/webui_create_app_config_categories.sh" + [webuiCreateCategories]="webui/data/generators/categories/webui_create_all_categories.sh" + [webuiCreateLogsFolders]="webui/data/logs/webui_logs_folders.sh" + [webuiCreateUpdateLock]="webui/data/lock/webui_create_update_lock.sh" + [webuiDisplayLogins]="webui/webui_display_logins.sh" + [webuiEnsureTaskFiles]="webui/data/tasks/webui_task_files.sh" + [webuiGenerateAppLogs]="webui/data/logs/webui_app_logs.sh" + [webuiGenerateAppsServicesConfig]="webui/data/generators/apps/webui_services.sh" + [webuiGenerateAppsToolsConfig]="webui/data/generators/apps/webui_tools.sh" + [webuiGenerateBackupAppStatus]="webui/data/generators/backup/webui_backup_app_status.sh" + [webuiGenerateBackupDashboard]="webui/data/generators/backup/webui_backup_dashboard.sh" + [webuiGenerateBackupEngines]="webui/data/generators/backup/webui_backup_engines.sh" + [webuiGenerateBackupLocations]="webui/data/generators/backup/webui_backup_locations.sh" + [webuiGenerateBackupMigrate]="webui/data/generators/backup/webui_backup_migrate.sh" + [webuiGenerateBackupPasswords]="webui/data/generators/backup/webui_backup_passwords.sh" + [webuiGenerateBackupSchema]="webui/data/generators/backup/webui_backup_schema.sh" + [webuiGenerateBackupSnapshots]="webui/data/generators/backup/webui_backup_snapshots.sh" + [webuiGenerateLibrePortalConfig]="webui/data/generators/apps/webui_config.sh" + [webuiGeneratePeers]="webui/data/generators/peers/webui_peers.sh" + [webuiGenerateSshAccess]="webui/data/generators/system/webui_ssh_access.sh" + [webuiGenerateSystemConfigs]="webui/data/generators/config/webui_generate_configs.sh" + [webuiLibrePortalUpdate]="webui/webui_updater.sh" + [webuiPatchAppConfigJson]="webui/data/generators/apps/webui_config_patch.sh" + [webuiPrintInstallCard]="webui/webui_display_logins.sh" + [webuiPrintLoginBlock]="webui/webui_display_logins.sh" + [_webuiReadServiceTags]="webui/data/generators/apps/webui_config.sh" + [webuiRemoveSetupLock]="webui/data/lock/webui_remove_setup_lock.sh" + [webuiRemoveUpdateLock]="webui/data/lock/webui_remove_update_lock.sh" + [webuiRunUpdate]="update/check_update.sh" + [webuiSetConfigOptions]="webui/data/generators/config/webui_cli_config_set.sh" + [webuiSyncAppIcon]="webui/data/utils/webui_app_icons.sh" + [webuiSyncAppIcons]="webui/data/utils/webui_app_icons.sh" + [webuiSystemDisk]="webui/data/generators/system/webui_system_disk.sh" + [webuiSystemInfo]="webui/data/generators/system/webui_system_info.sh" + [webuiSystemMemory]="webui/data/generators/system/webui_system_memory.sh" + [webuiSystemUpdate]="webui/data/generators/system/webui_system_update.sh" + [webuiSystemUpdateCheck]="webui/data/generators/system/webui_system_update.sh" + [webuiUpdateAppLog]="webui/data/utils/webui_app_log.sh" + [webuiUpdateAppStatus]="webui/data/generators/apps/webui_app_status.sh" + [webuiUpdateSystemConfig]="webui/data/generators/config/webui_update_config.sh" + [webuiValidateConfigValue]="webui/data/generators/config/webui_update_config.sh" + [whitelistPortUpdater]="docker/whitelist_port_updater.sh" + [writeAtomic]="crontab/task/crontab_task_processor.sh" + [zipFile]="function/file/zip_file.sh" +) + +# Files with top-level side effects (variable assignments, source calls, +# command invocations outside any function). Lazy mode MUST source these +# unconditionally — deferring them would skip the side effect, not just +# defer a function definition. +LP_EAGER_FILES=( + "backup/db/backup_db.sh" + "backup/files/backup_files.sh" + "cli/commands/debug/cli_debug_commands.sh" + "crontab/task/crontab_check_processor.sh" + "crontab/task/crontab_task_processor.sh" + "docker/type_switcher/swap_docker_type.sh" + "migrate/migrate_url_rewrite.sh" + "setup/setup_lock.sh" + "source/files/arrays/files_app.sh" + "source/files/arrays/files_backup.sh" + "source/files/arrays/files_checks.sh" + "source/files/arrays/files_cli.sh" + "source/files/arrays/files_config.sh" + "source/files/arrays/files_crontab.sh" + "source/files/arrays/files_database.sh" + "source/files/arrays/files_docker.sh" + "source/files/arrays/files_function.sh" + "source/files/arrays/files_install.sh" + "source/files/arrays/files_logs.sh" + "source/files/arrays/files_menu.sh" + "source/files/arrays/files_migrate.sh" + "source/files/arrays/files_network.sh" + "source/files/arrays/files_os.sh" + "source/files/arrays/files_peer.sh" + "source/files/arrays/files_restore.sh" + "source/files/arrays/files_setup.sh" + "source/files/arrays/files_source.sh" + "source/files/arrays/files_ssh.sh" + "source/files/arrays/files_start.sh" + "source/files/arrays/files_update.sh" + "source/files/arrays/files_webui.sh" + "source/files/arrays/function_manifest.sh" +) diff --git a/scripts/source/files/generate_function_manifest.sh b/scripts/source/files/generate_function_manifest.sh new file mode 100644 index 0000000..d1f3a17 --- /dev/null +++ b/scripts/source/files/generate_function_manifest.sh @@ -0,0 +1,230 @@ +#!/bin/bash + +# Function manifest generator — sidekick to generate_arrays.sh, supports the +# lazy-load path. Scans every script under scripts/ for top-level function +# definitions and writes scripts/source/files/arrays/function_manifest.sh: +# +# declare -gA LP_FN_MAP=( +# [funcname]="rel/path/to/file.sh" +# ... +# ) +# LP_EAGER_FILES=( "rel/path1.sh" "rel/path2.sh" ... ) +# +# LP_FN_MAP is what the lazy loader uses to install autoload stubs: +# funcname() { source "$install_scripts_dir${LP_FN_MAP[funcname]}"; funcname "$@"; } +# +# LP_EAGER_FILES are files with side effects at source time (set vars, run +# commands, etc.) that the lazy loader MUST source unconditionally — skipping +# them would skip the side effect, not just defer a function definition. +# +# Heuristic for eager detection (pragmatic, not a real bash parser): +# - Walk the file line-by-line, tracking { } depth to know "inside function". +# - A file is LAZY-SAFE iff every non-blank/non-comment line outside +# functions is either: (a) a function header `funcname() [{`, (b) `}` +# closing a function, or (c) a `local`/`declare` only inside functions. +# - Anything else at depth 0 (assignments, source calls, bare commands) → +# mark file EAGER. False positives are harmless (file just stays eager- +# loaded, same as today). False negatives WOULD be bugs, so the heuristic +# errs on the safe side. +# +# Collisions: if two files define the same function name, the LAST scan wins +# in LP_FN_MAP (matches what eager loading does — last source wins). All +# collisions are reported to stderr so they can be audited. +# +# Usage: ./generate_function_manifest.sh run +# +# SAFETY: only runs when executed directly with 'run' (mirrors generate_arrays.sh). + +if [[ "${BASH_SOURCE[0]}" == "${0}" && "$1" == "run" ]]; then + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ARRAYS_DIR="$SCRIPT_DIR/arrays" +SCRIPTS_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" +OUTPUT="$ARRAYS_DIR/function_manifest.sh" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; NC='\033[0m' +isSuccessful() { echo -e "${GREEN}✓ Success${NC} $1"; } +isNotice() { echo -e "${YELLOW}! Notice${NC} $1"; } +isError() { echo -e "${RED}✗ Error${NC} $1" >&2; } + +# Skip-list mirrors generate_arrays.sh — these are either deployment targets +# (peer/peer_shell.sh runs standalone via sshd's forced-command, never sourced), +# or build infrastructure that the loader bootstraps separately. +should_skip() { + local rel="$1" + case "$rel" in + source/files/app_files.sh|source/files/cli_files.sh) return 0 ;; + source/files/generate_arrays.sh|source/files/generate_function_manifest.sh) return 0 ;; + source/loading/check_files.sh|source/loading/initilize_files.sh|source/loading/scan_files.sh) return 0 ;; + source/load_sources.sh|source/paths.sh) return 0 ;; + webui/data/generators/webui_test_generate.sh) return 0 ;; + peer/peer_shell.sh) return 0 ;; + unused/*|system/*|release/*) return 0 ;; + esac + return 1 +} + +# Walk a file. Outputs to two named-pipe-equivalent variables via stdout: +# fn: — a function definition was found +# eager: — file has top-level side effects +# +# Depth tracking: count `{` and `}` only when they appear at the start of a +# token (anchored), avoiding most false positives from strings/heredocs. +# That's good enough for the LibrePortal codebase style; cleaner files would +# need a real parser. Errs on the side of marking files eager. +analyze_file() { + local file="$1" + awk ' + # Skip shebang and pure comments — they have no semantic effect at source. + /^#!/ { next } + /^[[:space:]]*#/ { next } + /^[[:space:]]*$/ { next } + + { + line = $0 + stripped = line + sub(/^[[:space:]]+/, "", stripped) + sub(/[[:space:]]+$/, "", stripped) + + # Function header: POSIX `funcname()` with EMPTY parens. We require + # the empty parens so that lines like `if (...)`, `for (...)`, + # `while (...)` are NOT misread as function definitions when we + # scan files that contain embedded awk/perl/other code. Whatever + # follows the `)` can be the opening `{`, a one-liner body, or + # just a newline. + is_fn_paren = (stripped ~ /^[A-Za-z_][A-Za-z0-9_]*[[:space:]]*\([[:space:]]*\)/) + is_fn_kw = (stripped ~ /^function[[:space:]]+[A-Za-z_][A-Za-z0-9_]*/) + if (depth == 0 && (is_fn_paren || is_fn_kw)) { + # Extract the name (everything up to `(` or trailing whitespace). + name = stripped + sub(/[[:space:]]*\(.*$/, "", name) + sub(/^function[[:space:]]+/, "", name) + sub(/[[:space:]]+.*$/, "", name) + print "fn:" name + # Net brace balance on THIS line: every { adds 1, every } subtracts. + # One-liner `name() { body; }` has equal counts → depth stays 0. + # Multi-line opener `name() {` has +1 → depth becomes 1. + # Brace-on-next-line `name()` has 0 → set expecting_open. + tmp = stripped + n_open = gsub(/\{/, "{", tmp) + tmp = stripped + n_close = gsub(/\}/, "}", tmp) + delta = n_open - n_close + if (delta > 0) depth += delta + else if (n_open == 0) expecting_open = 1 + next + } + + # Bare `{` at depth 0 after a function header is the continuation + # of that header (`name()` then newline then `{`). + if (depth == 0 && expecting_open && stripped == "{") { + depth++ + expecting_open = 0 + next + } + + # Closing brace at depth 1 ends a function. + if (depth > 0 && stripped == "}") { depth--; next } + + # Track depth roughly for content inside functions. We only need + # to know if depth == 0 for the eager check; bumping on any `{` + # at end-of-line and decrementing on `}` keeps it close enough. + if (depth > 0) { + # heredocs, strings — ignore detailed accounting; the only + # thing that matters is staying > 0 until the closing }. + if (stripped ~ /\{[[:space:]]*$/) depth++ + # Multiple `}` on a line: count them. + n_close = gsub(/\}/, "&", stripped) + # don’t double-count the line we ate above + next + } + + # At depth 0 AND not a recognised function header → side effect. + print "eager:" + # Keep scanning to find any further function defs in the file. + } + ' "$file" +} + +mkdir -p "$ARRAYS_DIR" + +declare -A fn_to_file +declare -A fn_collisions # name -> "file1\tfile2..." +declare -a eager_files + +total_files=0 +total_fns=0 + +# Find all .sh under scripts/ (no symlinks, no hidden). +while IFS= read -r -d '' file; do + rel=$(realpath --relative-to="$SCRIPTS_DIR" "$file") + should_skip "$rel" && continue + + total_files=$((total_files + 1)) + + is_eager=0 + while IFS= read -r tag; do + case "$tag" in + fn:*) + name="${tag#fn:}" + if [[ -n "${fn_to_file[$name]:-}" && "${fn_to_file[$name]}" != "$rel" ]]; then + fn_collisions[$name]="${fn_collisions[$name]:-${fn_to_file[$name]}}"$'\t'"$rel" + fi + fn_to_file[$name]="$rel" + total_fns=$((total_fns + 1)) + ;; + eager:) + is_eager=1 + ;; + esac + done < <(analyze_file "$file") + + (( is_eager )) && eager_files+=("$rel") +done < <(find "$SCRIPTS_DIR" -type f -name '*.sh' -print0) + +# Emit the manifest. +{ + printf '#!/bin/bash\n\n' + printf '# This file is auto-generated by generate_function_manifest.sh\n' + printf '# Do not edit manually — run\n' + printf '# ./scripts/source/files/generate_function_manifest.sh run\n\n' + + printf '# Function name → relative path. Used by the lazy loader (LP_LAZY=1)\n' + printf '# to install an autoload stub for each public function. First call to a\n' + printf '# stub sources the real file, which redefines the function with the real\n' + printf '# body; subsequent calls hit the real one directly.\n' + printf 'declare -gA LP_FN_MAP=(\n' + # Sort for stable diff output. + while IFS= read -r name; do + printf ' [%s]="%s"\n' "$name" "${fn_to_file[$name]}" + done < <(printf '%s\n' "${!fn_to_file[@]}" | sort) + printf ')\n\n' + + printf '# Files with top-level side effects (variable assignments, source calls,\n' + printf '# command invocations outside any function). Lazy mode MUST source these\n' + printf '# unconditionally — deferring them would skip the side effect, not just\n' + printf '# defer a function definition.\n' + printf 'LP_EAGER_FILES=(\n' + while IFS= read -r f; do + printf ' "%s"\n' "$f" + done < <(printf '%s\n' "${eager_files[@]}" | sort -u) + printf ')\n' +} > "$OUTPUT" + +isSuccessful "Wrote $(realpath --relative-to="$SCRIPTS_DIR" "$OUTPUT")" +isNotice "Scanned $total_files files, indexed $total_fns function definitions" +isNotice "${#eager_files[@]} files flagged eager (will always source)" + +# Collisions: report so they can be audited. The manifest reflects last-write- +# wins, which matches the existing eager-load semantics, so behaviour is +# identical — the warnings are about *avoidable* fragility, not bugs. +if (( ${#fn_collisions[@]} > 0 )); then + isNotice "Function name collisions (last write wins, matches eager-load behaviour):" + while IFS= read -r name; do + IFS=$'\t' read -ra files <<< "${fn_collisions[$name]}" + printf ' %s\n' "$name" + for f in "${files[@]}"; do printf ' - %s\n' "$f"; done + done < <(printf '%s\n' "${!fn_collisions[@]}" | sort) +fi + +fi diff --git a/scripts/webui/webui_regen.sh b/scripts/webui/webui_regen.sh index 6a90b0c..f507171 100644 --- a/scripts/webui/webui_regen.sh +++ b/scripts/webui/webui_regen.sh @@ -50,13 +50,20 @@ lpRegenArrays() { local force="$1" local arrays_dir="${install_scripts_dir}source/files/arrays" local gen_script="${install_scripts_dir}source/files/generate_arrays.sh" + local manifest_script="${install_scripts_dir}source/files/generate_function_manifest.sh" local newest_array newest_array="$(ls -t "$arrays_dir"/files_*.sh 2>/dev/null | head -1)" if [[ "$force" == "force" ]] || [[ -z "$newest_array" ]] \ || find "$install_scripts_dir" -name '*.sh' -newer "$newest_array" -print -quit 2>/dev/null | grep -q .; then - [[ -f "$gen_script" ]] && "$gen_script" run - return $? + local rc=0 + [[ -f "$gen_script" ]] && "$gen_script" run || rc=$? + # Function manifest tracks the same source set — keep them in sync. + # Failures here don't abort: lazy-load is opt-in via LP_LAZY=1, so a + # stale manifest just means lazy mode might miss a recently-added + # function. Eager mode (the default) is unaffected. + [[ -f "$manifest_script" ]] && "$manifest_script" run >/dev/null 2>&1 || true + return $rc fi return 0 }