From 2e349ebac1d65bc0413e3cd99cbb3e3fb026eba3 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Fri, 22 Dec 2023 09:33:06 +0300 Subject: [PATCH] Preview implementation of verbose offline validation --- src/alice/aliceswi.h | 60 ++++--- src/alice/exe.cpp | 2 +- src/alice/tdr.cpp | 2 +- src/burp/burp.cpp | 2 +- src/burp/restore.epp | 4 +- src/common/UtilSvc.cpp | 4 +- src/common/UtilSvc.h | 2 +- src/include/firebird/impl/consts_pub.h | 1 + src/include/gen/Firebird.pas | 1 + src/jrd/EngineInterface.h | 10 +- src/jrd/jrd.cpp | 14 +- src/jrd/svc.cpp | 222 +++++++++++++++++++++---- src/jrd/svc.h | 14 +- src/jrd/val_proto.h | 2 +- src/jrd/validation.cpp | 11 +- src/jrd/validation.h | 2 +- src/utilities/fbsvcmgr/fbsvcmgr.cpp | 1 + src/utilities/gsec/gsec.cpp | 2 +- src/utilities/gstat/dba.epp | 2 +- 19 files changed, 274 insertions(+), 84 deletions(-) diff --git a/src/alice/aliceswi.h b/src/alice/aliceswi.h index 8a0880b2aa1..a5b4058d6a0 100644 --- a/src/alice/aliceswi.h +++ b/src/alice/aliceswi.h @@ -66,11 +66,17 @@ const SINT64 sw_nolinger = QUADCONST(0x0000001000000000); const SINT64 sw_icu = QUADCONST(0x0000002000000000); const SINT64 sw_role = QUADCONST(0x0000004000000000); const SINT64 sw_replica = QUADCONST(0x0000008000000000); -const SINT64 sw_upgrade = QUADCONST(0x0000010000000000); +const SINT64 sw_upgrade = QUADCONST(0x0000010000000000); // Byte 5, Bit 0 +const SINT64 sw_progress = QUADCONST(0x0000020000000000); -// Popular combination of compatible switches +// Popular combinations of compatible switches const SINT64 sw_auth_set = sw_user | sw_password | sw_role | sw_fetch_password | sw_trusted_auth; - +const SINT64 sw_progress_dis = sw_sweep | sw_list | sw_prompt | sw_commit | sw_rollback | sw_shut | sw_two_phase | + sw_activate | sw_housekeeping | sw_kill | sw_write | sw_no_reserve | sw_shut | + sw_online | sw_buffers | sw_mode | sw_set_db_dialect | sw_replica | sw_nolinger | + sw_icu | sw_upgrade; +const ULONG ALICE_PROGRESS_MASK = isc_spb_rpr_validate_db | isc_spb_rpr_mend_db | isc_spb_rpr_check_db | + isc_spb_rpr_full | isc_spb_rpr_ignore_checksum | isc_spb_rpr_progress; enum alice_switches { @@ -128,7 +134,8 @@ enum alice_switches IN_SW_ALICE_ROLE = 49, IN_SW_ALICE_REPLICA = 50, IN_SW_ALICE_PARALLEL_WORKERS = 51, - IN_SW_ALICE_UPGRADE = 52 + IN_SW_ALICE_UPGRADE = 52, + IN_SW_ALICE_PROGRESS = 53 }; static const char* const ALICE_SW_ASYNC = "ASYNC"; @@ -147,7 +154,7 @@ static const char* const ALICE_SW_SHUT_FULL = "FULL"; static const Switches::in_sw_tab_t alice_in_sw_table[] = { {IN_SW_ALICE_ACTIVATE, isc_spb_prp_activate, "ACTIVATE_SHADOW", sw_activate, - 0, ~(sw_activate | sw_auth_set | sw_nolinger), false, true, 25, 2, NULL}, + 0, ~(sw_activate | sw_auth_set | sw_nolinger | sw_progress), false, true, 25, 2, NULL}, // msg 25: \t-activate shadow file for database usage {IN_SW_ALICE_ATTACH, isc_spb_prp_attachments_shutdown, "ATTACH", sw_attach, sw_shut, 0, false, false, 26, 2, NULL}, @@ -160,10 +167,10 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = */ #endif {IN_SW_ALICE_BUFFERS, isc_spb_prp_page_buffers, "BUFFERS", sw_buffers, - 0, 0, false, false, 28, 1, NULL}, + 0, sw_progress, false, false, 28, 1, NULL}, // msg 28: \t-buffers\tset page buffers {IN_SW_ALICE_COMMIT, isc_spb_rpr_commit_trans, "COMMIT", sw_commit, - 0, ~(sw_commit | sw_auth_set | sw_nolinger), false, false, 29, 2, NULL}, + 0, ~(sw_commit | sw_auth_set | sw_nolinger | sw_progress), false, false, 29, 2, NULL}, // msg 29: \t-commit\t\tcommit transaction {IN_SW_ALICE_CACHE, 0, "CACHE", sw_cache, sw_shut, 0, false, false, 30, 2, NULL}, @@ -185,34 +192,34 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = 0, (sw_trusted_auth | sw_password), false, false, 119, 2, NULL}, // msg 119: -fetch_password fetch_password from file {IN_SW_ALICE_HOUSEKEEPING, isc_spb_prp_sweep_interval, "HOUSEKEEPING", sw_housekeeping, - 0, 0, false, false, 34, 1, NULL}, + 0, sw_progress, false, false, 34, 1, NULL}, // msg 34: \t-housekeeping\tset sweep interval {IN_SW_ALICE_IGNORE, isc_spb_rpr_ignore_checksum, "IGNORE", sw_ignore, 0, 0, false, true, 35, 1, NULL}, // msg 35: \t-ignore\t\tignore checksum errors {IN_SW_ALICE_ICU, isc_spb_rpr_icu, "ICU", sw_icu, - 0, sw_shut, false, true, 131, 3, NULL}, + 0, sw_shut | sw_progress, false, true, 131, 3, NULL}, // msg 131: \t-icu\t\tfix database to be usable with present ICU version {IN_SW_ALICE_KILL, isc_spb_rpr_kill_shadows, "KILL_SHADOW", sw_kill, - 0, 0, false, true, 36, 1, NULL}, + 0, sw_progress, false, true, 36, 1, NULL}, // msg 36: \t-kill\t\tkill all unavailable shadow files {IN_SW_ALICE_LIST, isc_spb_rpr_list_limbo_trans, "LIST", sw_list, - 0, ~(sw_list | sw_auth_set | sw_nolinger), false, true, 37, 1, NULL}, + 0, ~(sw_list | sw_auth_set | sw_nolinger | sw_progress), false, true, 37, 1, NULL}, // msg 37: \t-list\t\tshow limbo transactions {IN_SW_ALICE_MEND, isc_spb_rpr_mend_db, "MEND", sw_mend | sw_validate | sw_full, - 0, ~(sw_no_update | sw_auth_set | sw_nolinger), false, true, 38, 2, NULL}, + 0, ~(sw_no_update | sw_auth_set | sw_nolinger | sw_progress), false, true, 38, 2, NULL}, // msg 38: \t-mend\t\tprepare corrupt database for backup {IN_SW_ALICE_MODE, 0, "MODE", sw_mode, - 0, ~(sw_mode | sw_auth_set | sw_nolinger), false, false, 109, 2, NULL}, + 0, ~(sw_mode | sw_auth_set | sw_nolinger | sw_progress), false, false, 109, 2, NULL}, // msg 109: \t-mode\t\tread_only or read_write {IN_SW_ALICE_NOLINGER, isc_spb_prp_nolinger, "NOLINGER", sw_nolinger, - 0, sw_shut, false, true, 121, 3, NULL}, + 0, sw_shut | sw_progress, false, true, 121, 3, NULL}, // msg 121: -nolinger do not use linger on database this time (once) {IN_SW_ALICE_NO_UPDATE, isc_spb_rpr_check_db, "NO_UPDATE", sw_no_update, sw_validate, 0, false, true, 39, 1, NULL}, // msg 39: \t-no_update\tread-only validation (-v) {IN_SW_ALICE_ONLINE, isc_spb_prp_db_online, "ONLINE", sw_online, - 0, 0, false, true, 40, 1 , NULL}, + 0, sw_progress, false, true, 40, 1 , NULL}, // msg 40: \t-online\t\tdatabase online {IN_SW_ALICE_PROMPT, 0, "PROMPT", sw_prompt, sw_list, 0, false, false, 41, 2, NULL}, @@ -224,6 +231,9 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = 0, (sw_trusted_auth | sw_fetch_password), false, false, 42, 2, NULL}, // msg 42: \t-password\tdefault password + {IN_SW_ALICE_PROGRESS, isc_spb_rpr_progress, "PROGRESS", sw_progress, + sw_validate, sw_progress_dis, false, true, 0, 2, NULL}, + // msg 42: \t-password\tdefault password #ifdef DEV_BUILD /* {IN_SW_ALICE_QUIT_LOG, 0, "QUIT_LOG", sw_quit_log, @@ -232,29 +242,29 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = */ #endif {IN_SW_ALICE_REPLICA, isc_spb_prp_replica_mode, "REPLICA", sw_replica, - 0, ~(sw_replica | sw_auth_set | sw_nolinger), false, false, 134, 2, NULL}, + 0, ~(sw_replica | sw_auth_set | sw_nolinger | sw_progress), false, false, 134, 2, NULL}, // msg 134: -replica access mode {IN_SW_ALICE_ROLE, 0, "ROLE", sw_role, 0, 0, false, false, 132, 4, NULL}, // msg 132: -role set SQL role name {IN_SW_ALICE_ROLLBACK, isc_spb_rpr_rollback_trans, "ROLLBACK", sw_rollback, - 0, ~(sw_rollback | sw_auth_set | sw_nolinger), false, false, 44, 1, NULL}, + 0, ~(sw_rollback | sw_auth_set | sw_nolinger | sw_progress), false, false, 44, 1, NULL}, // msg 44: \t-rollback\trollback transaction {IN_SW_ALICE_SET_DB_SQL_DIALECT, isc_spb_prp_set_sql_dialect, "SQL_DIALECT", sw_set_db_dialect, - 0, 0, false, false, 111, 2, NULL}, + 0, sw_progress, false, false, 111, 2, NULL}, // msg 111: \t-SQL_dialect\t\set dataabse dialect n {IN_SW_ALICE_SWEEP, isc_spb_rpr_sweep_db, "SWEEP", sw_sweep, - 0, ~(sw_sweep | sw_auth_set | sw_nolinger), false, true, 45, 2, NULL}, + 0, ~(sw_sweep | sw_auth_set | sw_nolinger | sw_progress), false, true, 45, 2, NULL}, // msg 45: \t-sweep\t\tforce garbage collection {IN_SW_ALICE_SHUT, isc_spb_prp_shutdown_mode, "SHUTDOWN", sw_shut, - 0, ~(sw_shut | sw_attach | sw_cache | sw_force | sw_tran | sw_auth_set), + 0, ~(sw_shut | sw_attach | sw_cache | sw_force | sw_tran | sw_auth_set | sw_progress), false, false, 46, 2, NULL}, // msg 46: \t-shut\t\tshutdown {IN_SW_ALICE_TWO_PHASE, isc_spb_rpr_recover_two_phase, "TWO_PHASE", sw_two_phase, - 0, ~(sw_two_phase | sw_auth_set | sw_nolinger), false, false, 47, 2, NULL}, + 0, ~(sw_two_phase | sw_auth_set | sw_nolinger | sw_progress), false, false, 47, 2, NULL}, // msg 47: \t-two_phase\tperform automated two-phase recovery {IN_SW_ALICE_TRAN, isc_spb_prp_transactions_shutdown, "TRANSACTION", sw_tran, - sw_shut, 0, false, false, 48, 3, NULL}, + sw_shut, sw_progress, false, false, 48, 3, NULL}, // msg 48: \t-tran\t\tshutdown transaction startup #ifdef TRUSTED_AUTH {IN_SW_ALICE_TRUSTED_AUTH, 0, "TRUSTED", sw_trusted_auth, @@ -262,10 +272,10 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = // msg 115: -trusted use trusted authentication #endif {IN_SW_ALICE_UPGRADE, isc_spb_rpr_upgrade_db, "UPGRADE", sw_upgrade, - 0, ~(sw_upgrade | sw_user | sw_password | sw_nolinger | sw_role), false, true, 137, 2, NULL}, + 0, ~(sw_upgrade | sw_user | sw_password | sw_nolinger | sw_role | sw_progress), false, true, 137, 2, NULL}, // msg 137: \t-upgrade\t\tupgrade database ODS {IN_SW_ALICE_NO_RESERVE, 0, "USE", sw_no_reserve, - 0, ~(sw_no_reserve | sw_auth_set | sw_nolinger), false, false, 49, 1, NULL}, + 0, ~(sw_no_reserve | sw_auth_set | sw_nolinger | sw_progress), false, false, 49, 1, NULL}, // msg 49: \t-use\t\tuse full or reserve space for versions {IN_SW_ALICE_USER, 0, "USER", sw_user, 0, sw_trusted_auth, false, false, 50, 4, NULL}, @@ -274,7 +284,7 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] = 0, ~(sw_validate | sw_auth_set | sw_nolinger), false, true, 51, 1, NULL}, // msg 51: \t-validate\tvalidate database structure {IN_SW_ALICE_WRITE, 0, "WRITE", sw_write, - 0, ~(sw_write | sw_auth_set | sw_nolinger), false, false, 52, 1, NULL}, + 0, ~(sw_write | sw_auth_set | sw_nolinger | sw_progress), false, false, 52, 1, NULL}, // msg 52: \t-write\t\twrite synchronously or asynchronously #ifdef DEV_BUILD {IN_SW_ALICE_X, 0, "X", 0, diff --git a/src/alice/exe.cpp b/src/alice/exe.cpp index 326d260fa9e..e13f1d25e56 100644 --- a/src/alice/exe.cpp +++ b/src/alice/exe.cpp @@ -201,7 +201,7 @@ static void buildDpb(Firebird::ClumpletWriter& dpb, const SINT64 switches) AliceGlobals* tdgbl = AliceGlobals::getSpecific(); dpb.reset(isc_dpb_version1); dpb.insertTag(isc_dpb_gfix_attach); - tdgbl->uSvc->fillDpb(dpb); + tdgbl->uSvc->fillDpb(dpb, nullptr); if (switches & sw_sweep) { dpb.insertByte(isc_dpb_sweep, isc_dpb_records); diff --git a/src/alice/tdr.cpp b/src/alice/tdr.cpp index d40f67e75c5..bf7ff3f467a 100644 --- a/src/alice/tdr.cpp +++ b/src/alice/tdr.cpp @@ -188,7 +188,7 @@ bool TDR_attach_database(ISC_STATUS* status_vector, tdr* trans, const TEXT* path Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertTag(isc_dpb_no_garbage_collect); dpb.insertTag(isc_dpb_gfix_attach); - tdgbl->uSvc->fillDpb(dpb); + tdgbl->uSvc->fillDpb(dpb, nullptr); if (tdgbl->ALICE_data.ua_user) { dpb.insertString(isc_dpb_user_name, tdgbl->ALICE_data.ua_user, fb_strlen(tdgbl->ALICE_data.ua_user)); } diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp index 9db13af103f..e144d8440d4 100644 --- a/src/burp/burp.cpp +++ b/src/burp/burp.cpp @@ -1144,7 +1144,7 @@ int gbak(Firebird::UtilSvc* uSvc) Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertString(isc_dpb_gbak_attach, FB_VERSION, fb_strlen(FB_VERSION)); - uSvc->fillDpb(dpb); + uSvc->fillDpb(dpb, nullptr); const UCHAR* authBlock; unsigned int authSize = uSvc->getAuthBlock(&authBlock); diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 29cc217e217..45fc8e5d705 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -664,7 +664,7 @@ namespace // unnamed, private // Add the common DPB params to the two attach calls in RESTORE_restore() void add_access_dpb(BurpGlobals* tdgbl, Firebird::ClumpletWriter& dpb) { - tdgbl->uSvc->fillDpb(dpb); + tdgbl->uSvc->fillDpb(dpb, nullptr); const UCHAR* authBlock; unsigned int authSize = tdgbl->uSvc->getAuthBlock(&authBlock); @@ -991,7 +991,7 @@ void create_database(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TE page_buffers = tdgbl->gbl_sw_page_buffers; Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); - tdgbl->uSvc->fillDpb(dpb); + tdgbl->uSvc->fillDpb(dpb, nullptr); const UCHAR* authBlock; unsigned int authSize = tdgbl->uSvc->getAuthBlock(&authBlock); diff --git a/src/common/UtilSvc.cpp b/src/common/UtilSvc.cpp index 423ec5ec32d..e64bcb325b0 100644 --- a/src/common/UtilSvc.cpp +++ b/src/common/UtilSvc.cpp @@ -39,6 +39,8 @@ namespace Firebird { +class IProvider; + namespace { void outputFile(FILE* f, const void* text, size_t len) { @@ -142,7 +144,7 @@ class StandaloneUtilityInterface : public UtilSvc void setServiceStatus(const ISC_STATUS*) override { } void setServiceStatus(const USHORT, const USHORT, const MsgFormat::SafeArg&) override { } StatusAccessor getStatusAccessor() override { return StatusAccessor(); } - void fillDpb(ClumpletWriter&) override { } + void fillDpb(ClumpletWriter&, IProvider* provider) override { } bool finished() override { return false; } bool utf8FileNames() override { return false; } Firebird::ICryptKeyCallback* getCryptCallback() override { return NULL; } diff --git a/src/common/UtilSvc.h b/src/common/UtilSvc.h index 353fb58fabd..4237bb9ea11 100644 --- a/src/common/UtilSvc.h +++ b/src/common/UtilSvc.h @@ -141,7 +141,7 @@ class UtilSvc : public Firebird::GlobalStorage virtual StatusAccessor getStatusAccessor() = 0; virtual void checkService() = 0; virtual void hidePasswd(ArgvType&, int) = 0; - virtual void fillDpb(Firebird::ClumpletWriter& dpb) = 0; + virtual void fillDpb(Firebird::ClumpletWriter& dpb, Firebird::IProvider* provider) = 0; virtual bool finished() = 0; virtual unsigned int getAuthBlock(const unsigned char** bytes) = 0; virtual bool utf8FileNames() = 0; diff --git a/src/include/firebird/impl/consts_pub.h b/src/include/firebird/impl/consts_pub.h index 961396be26d..8cc51926310 100644 --- a/src/include/firebird/impl/consts_pub.h +++ b/src/include/firebird/impl/consts_pub.h @@ -539,6 +539,7 @@ #define isc_spb_rpr_full 0x80 #define isc_spb_rpr_icu 0x0800 #define isc_spb_rpr_upgrade_db 0x1000 +#define isc_spb_rpr_progress 0x2000 /***************************************** * Parameters for isc_action_svc_restore * diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 1c39bd91243..473833b5ff8 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -4281,6 +4281,7 @@ IProfilerStatsImpl = class(IProfilerStats) isc_spb_rpr_full = $80; isc_spb_rpr_icu = $0800; isc_spb_rpr_upgrade_db = $1000; + isc_spb_rpr_progress = $2000; isc_spb_res_buffers = byte(9); isc_spb_res_page_size = byte(10); isc_spb_res_length = byte(11); diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 3f06df7e2eb..68c8c269b6a 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -28,6 +28,10 @@ #include "../common/StatementMetadata.h" #include "../common/classes/RefCounted.h" +namespace Firebird { +class UtilSvc; +} + namespace Jrd { // Engine objects used by interface objects @@ -550,9 +554,13 @@ class JProvider final : void setDbCryptCallback(Firebird::CheckStatusWrapper* status, Firebird::ICryptKeyCallback* cryptCb); + // Used by services + JAttachment* attachAndValidate(Firebird::CheckStatusWrapper* user_status, const char* filename, + unsigned int dpb_length, const unsigned char* dpb, Firebird::UtilSvc* uSvc); + private: JAttachment* internalAttach(Firebird::CheckStatusWrapper* status, const char* const fileName, - unsigned int dpbLength, const unsigned char* dpb, const UserId* existingId); + unsigned int dpbLength, const unsigned char* dpb, const UserId* existingId, Firebird::UtilSvc* uSvc); Firebird::ICryptKeyCallback* cryptCallback; Firebird::IPluginConfig* pluginConfig; }; diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 5f03b3bb8dc..3aef06aab24 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1603,11 +1603,17 @@ jrd_tra* JAttachment::getEngineTransaction(CheckStatusWrapper* status, ITransact JAttachment* JProvider::attachDatabase(CheckStatusWrapper* user_status, const char* filename, unsigned int dpb_length, const unsigned char* dpb) { - return internalAttach(user_status, filename, dpb_length, dpb, NULL); + return internalAttach(user_status, filename, dpb_length, dpb, NULL, NULL); +} + +JAttachment* JProvider::attachAndValidate(CheckStatusWrapper* user_status, const char* filename, + unsigned int dpb_length, const unsigned char* dpb, UtilSvc* uSvc) +{ + return internalAttach(user_status, filename, dpb_length, dpb, NULL, uSvc); } JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const char* const filename, - unsigned int dpb_length, const unsigned char* dpb, const UserId* existingId) + unsigned int dpb_length, const unsigned char* dpb, const UserId* existingId, UtilSvc* uSvc) { /************************************** * @@ -2086,7 +2092,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch AutoSetRestoreFlag noCleanup(&attachment->att_flags, ATT_no_cleanup, true); VIO_fini(tdbb); - if (!VAL_validate(tdbb, options.dpb_verify)) + if (!VAL_validate(tdbb, options.dpb_verify, uSvc)) ERR_punt(); } @@ -3026,7 +3032,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch OverwriteHolder overwriteCheckHolder(dbb); JAttachment* attachment2 = internalAttach(user_status, filename, dpb_length, - dpb, &userId); + dpb, &userId, NULL); switch (user_status->getErrors()[1]) { case isc_adm_task_denied: diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 1f87f106349..7da1e6f1a6f 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -182,6 +182,8 @@ const serv_entry services[] = { 0, NULL, NULL } }; +const serv_entry valWithProgress = + { 0, NULL, Service::repair }; } Service::Validate::Validate(Service* svc) @@ -599,7 +601,7 @@ unsigned int Service::getAuthBlock(const unsigned char** bytes) return svc_auth_block.getCount(); } -void Service::fillDpb(ClumpletWriter& dpb) +void Service::fillDpb(ClumpletWriter& dpb, IProvider* provider) { dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); if (svc_address_path.hasData()) @@ -613,10 +615,18 @@ void Service::fillDpb(ClumpletWriter& dpb) if (svc_crypt_callback) { // That's not DPB-related, but anyway should be done before attach/create DB - ISC_STATUS_ARRAY status; - if (fb_database_crypt_callback(status, svc_crypt_callback) != 0) + if (provider) { - status_exception::raise(status); + FbLocalStatus status; + provider->setDbCryptCallback(&status, svc_crypt_callback); + if (!status.isSuccess()) + status_exception::raise(&status); + } + else + { + ISC_STATUS_ARRAY status; + if (fb_database_crypt_callback(status, svc_crypt_callback) != 0) + status_exception::raise(status); } } if (svc_remote_process.hasData()) @@ -694,7 +704,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d svc_resp_alloc(getPool()), svc_resp_buf(0), svc_resp_ptr(0), svc_resp_buf_len(0), svc_resp_len(0), svc_flags(SVC_finished), svc_user_flag(0), svc_spb_version(0), svc_shutdown_server(false), svc_shutdown_request(false), - svc_shutdown_in_progress(false), svc_timeout(false), + svc_shutdown_in_progress(false), svc_timeout(false), svc_rpr_options(0), svc_username(getPool()), svc_sql_role(getPool()), svc_auth_block(getPool()), svc_expected_db(getPool()), svc_trusted_role(false), svc_utf8(false), svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()), @@ -2044,7 +2054,7 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data) if (flNeedUser) { - // add the username to the end of svc_switches if needed + // add the username to svc_switches if needed if (svc_switches.hasData() && !svc_auth_block.hasData()) { if (svc_username.hasData()) @@ -2087,6 +2097,11 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data) status_exception::raise(Arg::Gds(isc_adm_task_denied) << Arg::Gds(isc_not_dba)); } + if (svc_rpr_options) + { + svc_service_run = &valWithProgress; + } + // Break up the command line into individual arguments. parseSwitches(); @@ -2096,40 +2111,33 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data) svc_status->init(); } - if (serv->serv_thd) - { - svc_flags &= ~(SVC_evnt_fired | SVC_finished); + svc_flags &= ~(SVC_evnt_fired | SVC_finished); - svc_stdout_head = svc_stdout_tail = 0; + svc_stdout_head = svc_stdout_tail = 0; - Thread::start(run, this, THREAD_medium, &svc_thread); + Thread::start(run, this, THREAD_medium, &svc_thread); - // good time for housekeeping while new thread starts - threadCollect->houseKeeping(); + // good time for housekeeping while new thread starts + threadCollect->houseKeeping(); - // Check for the service being detached. This will prevent the thread - // from waiting infinitely if the client goes away. - while (!(svc_flags & SVC_detached)) + // Check for the service being detached. This will prevent the thread + // from waiting infinitely if the client goes away. + while (!(svc_flags & SVC_detached)) + { + // The semaphore will be released once the particular service + // has reached a point in which it can start to return + // information to the client. This will allow isc_service_start + // to include in its status vector information about the service's + // ability to start. + // This is needed since Thread::start() will almost always succeed. + // + // Do not unlock mutex here - one can call start not doing this. + if (svcStart.tryEnter(60)) { - // The semaphore will be released once the particular service - // has reached a point in which it can start to return - // information to the client. This will allow isc_service_start - // to include in its status vector information about the service's - // ability to start. - // This is needed since Thread::start() will almost always succeed. - // - // Do not unlock mutex here - one can call start not doing this. - if (svcStart.tryEnter(60)) - { - // started() was called - break; - } + // started() was called + break; } } - else - { - status_exception::raise(Arg::Gds(isc_svcnotdef) << Arg::Str(serv->serv_name)); - } } // try catch (const Firebird::Exception& ex) @@ -2225,6 +2233,129 @@ void Service::readFbLog() } +int Service::repair(UtilSvc* arg) +{ + Service* service = (Service*) arg; + service->repair(); + return 0; +} + +static void internalError() +{ + (Arg::Gds(isc_random) << "internal Service::repair() error").raise(); +} + +void Service::repair() +{ + try + { + { + MutexLockGuard g(svc_status_mutex, FB_FUNCTION); + svc_status->init(); + } + + PathName dbName; + int options = isc_dpb_pages | isc_dpb_records; + + ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); + dpb.insertTag(isc_dpb_no_garbage_collect); + + const Switches valSwitches(alice_in_sw_table, FB_NELEM(alice_in_sw_table), false, true); + const char** av = argv.begin(); + const char* const* end = argv.end(); + + for (++av; av < end; av++) + { + if (!*av) + continue; + + const Switches::in_sw_tab_t* sw = valSwitches.findSwitch(*av); + if (!sw) + { + if (dbName.isEmpty()) + dbName = *av; + else + internalError(); + + continue; + } + + switch (sw->in_sw) + { + case IN_SW_ALICE_MEND: + options |= isc_dpb_repair; + break; + + case IN_SW_ALICE_FULL: + case IN_SW_ALICE_VALIDATE: + case IN_SW_ALICE_PROGRESS: + break; + + case IN_SW_ALICE_NO_UPDATE: + break; + options |= isc_dpb_no_update; + + case IN_SW_ALICE_IGNORE: + options |= isc_dpb_ignore; + break; + + case IN_SW_ALICE_PARALLEL_WORKERS: + av++; + if (av < end && *av) + dpb.insertInt(isc_dpb_parallel_workers, atoi(*av)); + else + internalError(); + break; + + case IN_SW_ALICE_USER: + av++; + if (av < end && *av) + dpb.insertString(isc_dpb_user_name, *av); + else + internalError(); + break; + + case IN_SW_ALICE_ROLE: + av++; + if (av < end && *av) + dpb.insertString(isc_dpb_sql_role_name, *av); + else + internalError(); + break; + + default: + break; + } + } + + PathName expandedFilename; + if (expandDatabaseName(dbName, expandedFilename, NULL)) + expandedFilename = dbName; + if (dbName != expandedFilename) + dpb.insertString(isc_dpb_org_filename, dbName); + + dpb.insertInt(isc_dpb_verify, options); + + FbLocalStatus status; + AutoPlugin jProv(JProvider::getInstance()); + fillDpb(dpb, jProv); + + RefPtr jAtt(REF_NO_INCR, jProv->attachAndValidate(&status, + expandedFilename.c_str(), dpb.getBufferLength(), dpb.getBuffer(), this)); + + if (status->getState() & IStatus::STATE_ERRORS) + getStatusAccessor().setServiceStatus(status->getErrors()); + } + catch (const Firebird::Exception& e) + { + MutexLockGuard g(svc_status_mutex, FB_FUNCTION); + e.stuffException(&svc_status); + } + + started(); +} + + void Service::start(const serv_entry* service_run) { // Break up the command line into individual arguments. @@ -2621,6 +2752,8 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) bool found = false; string::size_type userPos = string::npos; + svc_rpr_options = 0; + do { bool bigint = false; @@ -3023,7 +3156,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) get_action_svc_string(spb, switches); break; case isc_spb_options: - if (!get_action_svc_bitmask(spb, alice_in_sw_table, switches)) + if (!get_action_svc_bitmask(spb, alice_in_sw_table, switches, &svc_rpr_options)) { return false; } @@ -3051,6 +3184,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) return false; } get_action_svc_data(spb, switches, bigint); + svc_rpr_options |= ~isc_spb_rpr_progress; break; case isc_spb_prp_write_mode: case isc_spb_prp_access_mode: @@ -3059,6 +3193,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) { return false; } + svc_rpr_options |= ~isc_spb_rpr_progress; break; case isc_spb_prp_shutdown_mode: case isc_spb_prp_online_mode: @@ -3071,6 +3206,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) } switches += alice_shut_mode_sw_table[val]; switches += " "; + svc_rpr_options |= ~isc_spb_rpr_progress; break; } return false; @@ -3084,6 +3220,7 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) } switches += alice_repl_mode_sw_table[val]; switches += " "; + svc_rpr_options |= ~isc_spb_rpr_progress; break; } return false; @@ -3165,6 +3302,17 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) status_exception::raise(Arg::Gds(isc_unexp_spb_form) << Arg::Str(SPB_SEC_USERNAME)); } + // postfix for gfix + if (svc_rpr_options & isc_spb_rpr_progress) + { + if (!(svc_rpr_options & isc_spb_rpr_validate_db)) + status_exception::raise(Arg::Gds(isc_random) << "Progress display requires DB validate switch"); + if (svc_rpr_options & ~ALICE_PROGRESS_MASK) + status_exception::raise(Arg::Gds(isc_random) << "Progress display incompatible with some switch"); + } + else + svc_rpr_options = 0; + // postfixes for burp & nbackup switch (svc_action) { @@ -3253,7 +3401,8 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) bool Service::get_action_svc_bitmask(const ClumpletReader& spb, const Switches::in_sw_tab_t* table, - string& switches) + string& switches, + ULONG* allOptions) { const int opt = spb.getInt(); ISC_ULONG mask = 1; @@ -3270,6 +3419,9 @@ bool Service::get_action_svc_bitmask(const ClumpletReader& spb, switches += '-'; switches += s_ptr; switches += ' '; + + if (allOptions) + *allOptions |= mask; } } diff --git a/src/jrd/svc.h b/src/jrd/svc.h index 66cf96790fd..abac469521b 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -144,7 +144,7 @@ class Service : public Firebird::UtilSvc, public TypedHandle // no-op for services void checkService() override; // add address path and utf8 flag (taken from spb) to dpb if present - void fillDpb(Firebird::ClumpletWriter& dpb) override; + void fillDpb(Firebird::ClumpletWriter& dpb, Firebird::IProvider* provider) override; // encoding for string parameters passed to utility bool utf8FileNames() override; // get database encryption key transfer callback routine @@ -186,11 +186,14 @@ class Service : public Firebird::UtilSvc, public TypedHandle return svc_spb_version; } + // "Built-in" services // Firebird log reader static int readFbLog(Firebird::UtilSvc* uSvc); + // Validate DB with progress output + static int repair(Firebird::UtilSvc* uSvc); + // Shuts all service threads (should be called after databases shutdown) static void shutdownServices(); - // Total number of service attachments static ULONG totalCount(); @@ -225,8 +228,9 @@ class Service : public Firebird::UtilSvc, public TypedHandle bool locateInAllServices(FB_SIZE_T* posPtr = NULL); // Detach self from global services list void removeFromAllServices(); - // The only service, implemented internally + // Services, implemented internally void readFbLog(); + void repair(); // Create argv, argc and svc_parsed_sw void parseSwitches(); // Check does this action need arg or not @@ -272,7 +276,8 @@ class Service : public Firebird::UtilSvc, public TypedHandle // add them to the command line static bool get_action_svc_bitmask(const Firebird::ClumpletReader& spb, const Switches::in_sw_tab_t* table, - Firebird::string& sw); + Firebird::string& sw, + ULONG* allOptions = nullptr); // Get string from within spb buffer, add it to the command line static void get_action_svc_string(const Firebird::ClumpletReader& spb, Firebird::string& sw); // Get string from within spb buffer, insert it at given position into command line @@ -318,6 +323,7 @@ class Service : public Firebird::UtilSvc, public TypedHandle bool svc_timeout; char svc_arg_conv[MsgFormat::SAFEARG_MAX_ARG * 2]; char* svc_arg_ptr; + ULONG svc_rpr_options; Firebird::string svc_username; Firebird::string svc_sql_role; diff --git a/src/jrd/val_proto.h b/src/jrd/val_proto.h index e7ebe2e02be..f640c5b93be 100644 --- a/src/jrd/val_proto.h +++ b/src/jrd/val_proto.h @@ -24,7 +24,7 @@ #ifndef JRD_VAL_PROTO_H #define JRD_VAL_PROTO_H -bool VAL_validate(Jrd::thread_db*, USHORT); +bool VAL_validate(Jrd::thread_db*, USHORT, Firebird::UtilSvc*); int VAL_service(Firebird::UtilSvc*); const int IN_SW_VAL_TAB_INCL = 1; diff --git a/src/jrd/validation.cpp b/src/jrd/validation.cpp index a30566642c6..963c402f1d5 100644 --- a/src/jrd/validation.cpp +++ b/src/jrd/validation.cpp @@ -653,7 +653,7 @@ static void explain_pp_bits(const UCHAR bits, Firebird::string& names) } -bool VAL_validate(thread_db* tdbb, USHORT switches) +bool VAL_validate(thread_db* tdbb, USHORT switches, Firebird::UtilSvc* uSvc) { /************************************** * @@ -670,7 +670,7 @@ bool VAL_validate(thread_db* tdbb, USHORT switches) Attachment* att = tdbb->getAttachment(); if (!att->att_validation) - att->att_validation = FB_NEW_POOL (*att->att_pool) Validation(tdbb); + att->att_validation = FB_NEW_POOL (*att->att_pool) Validation(tdbb, nullptr, uSvc); USHORT flags = 0; if (switches & isc_dpb_records) @@ -851,7 +851,7 @@ const Validation::MSG_ENTRY Validation::vdr_msg_table[VAL_MAX_ERROR] = {true, isc_info_dpage_errors, "Data page %" ULONGFORMAT" {sequence %" ULONGFORMAT"} marked as secondary but contains primary record versions"} }; -Validation::Validation(thread_db* tdbb, UtilSvc* uSvc) +Validation::Validation(thread_db* tdbb, UtilSvc* uSvc, UtilSvc* masterService) : vdr_cond_idx(*tdbb->getDefaultPool()), vdr_used_bdbs(*tdbb->getDefaultPool()) { @@ -870,7 +870,7 @@ Validation::Validation(thread_db* tdbb, UtilSvc* uSvc) vdr_idx_records = NULL; vdr_page_bitmap = NULL; - vdr_service = uSvc; + vdr_service = uSvc ? uSvc : masterService; vdr_lock_tout = -10; if (uSvc) { @@ -1014,6 +1014,9 @@ bool Validation::run(thread_db* tdbb, USHORT flags) Database* dbb = tdbb->getDatabase(); Firebird::PathName fileName = tdbb->getAttachment()->att_filename; + if (vdr_service) + vdr_service->started(); + try { val_pool = dbb->createPool(); diff --git a/src/jrd/validation.h b/src/jrd/validation.h index b2d56aaf19e..98811a2e4a8 100644 --- a/src/jrd/validation.h +++ b/src/jrd/validation.h @@ -171,7 +171,7 @@ class Validation void checkDPinPIP(jrd_rel *relation, ULONG page_number); public: - explicit Validation(thread_db*, Firebird::UtilSvc* uSvc = NULL); + explicit Validation(thread_db*, Firebird::UtilSvc* uSvc, Firebird::UtilSvc* masterService = nullptr); ~Validation(); bool run(thread_db* tdbb, USHORT flags); diff --git a/src/utilities/fbsvcmgr/fbsvcmgr.cpp b/src/utilities/fbsvcmgr/fbsvcmgr.cpp index 08f56211a23..f8c20b04a7b 100644 --- a/src/utilities/fbsvcmgr/fbsvcmgr.cpp +++ b/src/utilities/fbsvcmgr/fbsvcmgr.cpp @@ -504,6 +504,7 @@ const SvcSwitches repairOptions[] = {"rpr_icu", putOption, 0, isc_spb_rpr_icu, 0}, {"rpr_par_workers", putIntArgument, 0, isc_spb_rpr_par_workers, 0}, {"rpr_upgrade_db", putOption, 0, isc_spb_rpr_upgrade_db, 0}, + {"rpr_progress", putOption, 0, isc_spb_rpr_progress, isc_info_svc_line}, {0, 0, 0, 0, 0} }; diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index e29f3c9ef0e..7a708ab6b4d 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -419,7 +419,7 @@ int gsec(Firebird::UtilSvc* uSvc) // Get remote address info for management plugin Firebird::string network_protocol, remote_address; Firebird::ClumpletWriter tmp(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); - uSvc->fillDpb(tmp); + uSvc->fillDpb(tmp, nullptr); if (tmp.find(isc_dpb_address_path)) { diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index fde3b546dbc..e371ccbe5eb 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -776,7 +776,7 @@ int gstat(Firebird::UtilSvc* uSvc) // SYSDBA or owner of the database Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); - uSvc->fillDpb(dpb); + uSvc->fillDpb(dpb, nullptr); dpb.insertTag(isc_dpb_gstat_attach); dpb.insertTag(isc_dpb_no_garbage_collect);