diff options
author | Saturn&Eric <[email protected]> | 2024-01-23 07:21:28 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2024-01-23 07:21:28 +0000 |
commit | 56acf161d439ce73eceaa145c40fe703bb2c3f02 (patch) | |
tree | a5d4790a6b2efc8786a3c0f74a07f5a8032d4e94 /src/core/function/gpg/GpgCommandExecutor.cpp | |
parent | fix: use more secure cdn links (diff) | |
parent | fix: solve issues on detected gnupg path function and fix linking errors on w... (diff) | |
download | GpgFrontend-56acf161d439ce73eceaa145c40fe703bb2c3f02.tar.gz GpgFrontend-56acf161d439ce73eceaa145c40fe703bb2c3f02.zip |
Merge pull request #126 from saturneric/dev/2.1.1/main
Develop 2.1.2.1
Diffstat (limited to 'src/core/function/gpg/GpgCommandExecutor.cpp')
-rw-r--r-- | src/core/function/gpg/GpgCommandExecutor.cpp | 369 |
1 files changed, 174 insertions, 195 deletions
diff --git a/src/core/function/gpg/GpgCommandExecutor.cpp b/src/core/function/gpg/GpgCommandExecutor.cpp index 86c47c60..6d24f9bd 100644 --- a/src/core/function/gpg/GpgCommandExecutor.cpp +++ b/src/core/function/gpg/GpgCommandExecutor.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 Saturneric + * Copyright (C) 2021 Saturneric <[email protected]> * * This file is part of GpgFrontend. * @@ -20,252 +20,231 @@ * the gpg4usb project, which is under GPL-3.0-or-later. * * All the source code of GpgFrontend was modified and released by - * Saturneric<[email protected]> starting on May 12, 2021. + * Saturneric <[email protected]> starting on May 12, 2021. * * SPDX-License-Identifier: GPL-3.0-or-later * */ #include "GpgCommandExecutor.h" -#include "GpgFunctionObject.h" +#include "core/model/DataObject.h" +#include "core/module/Module.h" +#include "core/thread/Task.h" #include "core/thread/TaskRunnerGetter.h" -GpgFrontend::GpgCommandExecutor::GpgCommandExecutor(int channel) - : SingletonFunctionObject<GpgCommandExecutor>(channel) {} +namespace GpgFrontend { -void GpgFrontend::GpgCommandExecutor::Execute( - std::string cmd, std::vector<std::string> arguments, - std::function<void(int, std::string, std::string)> callback, - std::function<void(QProcess *)> interact_func) { - SPDLOG_DEBUG("called cmd {} arguments size: {}", cmd, arguments.size()); +auto BuildTaskFromExecCtx(const GpgCommandExecutor::ExecuteContext &context) + -> Thread::Task * { + const auto &cmd = context.cmd; + const auto &arguments = context.arguments; + const auto &interact_function = context.int_func; + const auto &cmd_executor_callback = context.cb_func; - Thread::Task::TaskCallback result_callback = - [](int rtn, Thread::Task::DataObjectPtr data_object) { - SPDLOG_DEBUG("data object use count: {}", data_object.use_count()); - if (data_object->GetObjectSize() != 4) - throw std::runtime_error("invalid data object size"); + const QString joined_argument = QStringList::fromVector(arguments).join(" "); + + GF_CORE_LOG_DEBUG("building task: called cmd {} arguments size: {}", cmd, + arguments.size()); - auto exit_code = data_object->PopObject<int>(); - auto process_stdout = data_object->PopObject<std::string>(); - auto process_stderr = data_object->PopObject<std::string>(); - auto callback = data_object->PopObject< - std::function<void(int, std::string, std::string)>>(); + Thread::Task::TaskCallback result_callback = + [cmd, joined_argument](int /*rtn*/, const DataObjectPtr &data_object) { + GF_CORE_LOG_DEBUG( + "data object args count of cmd executor result callback: {}", + data_object->GetObjectSize()); + if (!data_object->Check<int, QString, GpgCommandExecutorCallback>()) { + GF_CORE_LOG_ERROR("data object checking failed"); + return; + } + + auto exit_code = ExtractParams<int>(data_object, 0); + auto process_stdout = ExtractParams<QString>(data_object, 1); + auto callback = + ExtractParams<GpgCommandExecutorCallback>(data_object, 2); // call callback - callback(exit_code, process_stdout, process_stderr); + GF_CORE_LOG_DEBUG( + "calling custom callback from caller of cmd {} {}, " + "exit_code: {}", + cmd, joined_argument, exit_code); + callback(exit_code, process_stdout, {}); }; Thread::Task::TaskRunnable runner = - [](GpgFrontend::Thread::Task::DataObjectPtr data_object) -> int { - SPDLOG_DEBUG("process runner called, data object size: {}", - data_object->GetObjectSize()); + [joined_argument](const DataObjectPtr &data_object) -> int { + GF_CORE_LOG_DEBUG("process runner called, data object size: {}", + data_object->GetObjectSize()); - if (data_object->GetObjectSize() != 4) - throw std::runtime_error("invalid data object size"); + if (!data_object->Check<QString, QStringList, GpgCommandExecutorInteractor, + GpgCommandExecutorCallback>()) { + GF_CORE_LOG_ERROR("data object checking failed"); + return -1; + } // get arguments - auto cmd = data_object->PopObject<std::string>(); - SPDLOG_DEBUG("get cmd: {}", cmd); - auto arguments = data_object->PopObject<std::vector<std::string>>(); + auto cmd = ExtractParams<QString>(data_object, 0); + auto arguments = ExtractParams<QStringList>(data_object, 1); auto interact_func = - data_object->PopObject<std::function<void(QProcess *)>>(); + ExtractParams<GpgCommandExecutorInteractor>(data_object, 2); + auto callback = ExtractParams<GpgCommandExecutorCallback>(data_object, 3); + // create process auto *cmd_process = new QProcess(); + // move to current thread + // + cmd_process->moveToThread(QThread::currentThread()); + // set process channel mode + // this is to make sure we can get all output from stdout and stderr cmd_process->setProcessChannelMode(QProcess::MergedChannels); + cmd_process->setProgram(cmd); + + // set arguments + QStringList q_arguments; + for (const auto &argument : arguments) { + q_arguments.append(argument); + } + cmd_process->setArguments(q_arguments); - QObject::connect(cmd_process, &QProcess::started, - []() -> void { SPDLOG_DEBUG("process started"); }); + QObject::connect( + cmd_process, &QProcess::started, [cmd, joined_argument]() -> void { + GF_CORE_LOG_DEBUG( + "\n== Process Execute Started ==\nCommand: {}\nArguments: " + "{}\n========================", + cmd, joined_argument); + }); QObject::connect( cmd_process, &QProcess::readyReadStandardOutput, [interact_func, cmd_process]() { interact_func(cmd_process); }); - QObject::connect(cmd_process, &QProcess::errorOccurred, - [=](QProcess::ProcessError error) { - SPDLOG_ERROR("error in executing command: {} error: {}", - cmd, error); - }); QObject::connect( - cmd_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), - [=](int, QProcess::ExitStatus status) { - if (status == QProcess::NormalExit) - SPDLOG_DEBUG( - "proceess finished, succeed in executing command: {}, exit " - "status: {}", - cmd, status); - else - SPDLOG_ERROR( - "proceess finished, error in executing command: {}, exit " - "status: {}", - cmd, status); + cmd_process, &QProcess::errorOccurred, + [=](QProcess::ProcessError error) { + GF_CORE_LOG_ERROR( + "caught error while executing command: {} {}, error: {}", cmd, + joined_argument, error); }); - cmd_process->setProgram(QString::fromStdString(cmd)); - - QStringList q_arguments; - for (const auto &argument : arguments) - q_arguments.append(QString::fromStdString(argument)); - cmd_process->setArguments(q_arguments); - - SPDLOG_DEBUG("process execute ready, cmd: {} {}", cmd, - q_arguments.join(" ").toStdString()); + GF_CORE_LOG_DEBUG( + "\n== Process Execute Ready ==\nCommand: {}\nArguments: " + "{}\n========================", + cmd, joined_argument); cmd_process->start(); cmd_process->waitForFinished(); - std::string process_stdout = - cmd_process->readAllStandardOutput().toStdString(), - process_stderr = - cmd_process->readAllStandardError().toStdString(); + QString process_stdout = cmd_process->readAllStandardOutput(); int exit_code = cmd_process->exitCode(); + GF_CORE_LOG_DEBUG( + "\n==== Process Execution Summary ====\n" + "Command: {}\n" + "Arguments: {}\n" + "Exit Code: {}\n" + "---- Standard Output ----\n" + "{}\n" + "===============================", + cmd, joined_argument, exit_code, process_stdout); + cmd_process->close(); cmd_process->deleteLater(); - // transfer result - SPDLOG_DEBUG("runner append object"); - data_object->AppendObject(std::move(process_stderr)); - data_object->AppendObject(std::move(process_stdout)); - data_object->AppendObject(std::move(exit_code)); - SPDLOG_DEBUG("runner append object done"); - + data_object->Swap({exit_code, process_stdout, callback}); return 0; }; - // data transfer into task - auto data_object = std::make_shared<Thread::Task::DataObject>(); - SPDLOG_DEBUG("executor append object"); - data_object->AppendObject(std::move(callback)); - data_object->AppendObject(std::move(interact_func)); - data_object->AppendObject(std::move(arguments)); - data_object->AppendObject(std::move(std::string{cmd})); - SPDLOG_DEBUG("executor append object done"); - - auto *process_task = new GpgFrontend::Thread::Task( - std::move(runner), fmt::format("Execute/{}", cmd), data_object, + return new Thread::Task( + std::move(runner), QString("GpgCommamdExecutor(%1){%2}").arg(cmd), + TransferParams(cmd, arguments, interact_function, cmd_executor_callback), std::move(result_callback)); +} + +void GpgCommandExecutor::ExecuteSync(ExecuteContext context) { + Thread::Task *task = BuildTaskFromExecCtx(context); QEventLoop looper; - QObject::connect(process_task, &Thread::Task::SignalTaskEnd, &looper, + QObject::connect(task, &Thread::Task::SignalTaskEnd, &looper, &QEventLoop::quit); - GpgFrontend::Thread::TaskRunnerGetter::GetInstance() - .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_External_Process) - ->PostTask(process_task); - + Thread::TaskRunnerPtr target_task_runner = nullptr; + + if (context.task_runner != nullptr) { + target_task_runner = context.task_runner; + } else { + target_task_runner = + GpgFrontend::Thread::TaskRunnerGetter::GetInstance().GetTaskRunner( + Thread::TaskRunnerGetter::kTaskRunnerType_External_Process); + } + target_task_runner->PostTask(task); + + // to arvoid dead lock issue we need to check if current thread is the same as + // target thread. if it is, we can't call exec() because it will block the + // current thread. + GF_CORE_LOG_TRACE("blocking until gpg command finish..."); // block until task finished // this is to keep reference vaild until task finished looper.exec(); } -void GpgFrontend::GpgCommandExecutor::ExecuteConcurrently( - std::string cmd, std::vector<std::string> arguments, - std::function<void(int, std::string, std::string)> callback, - std::function<void(QProcess *)> interact_func) { - SPDLOG_DEBUG("called cmd {} arguments size: {}", cmd, arguments.size()); - - Thread::Task::TaskCallback result_callback = - [](int rtn, Thread::Task::DataObjectPtr data_object) { - if (data_object->GetObjectSize() != 4) - throw std::runtime_error("invalid data object size"); - - auto exit_code = data_object->PopObject<int>(); - auto process_stdout = data_object->PopObject<std::string>(); - auto process_stderr = data_object->PopObject<std::string>(); - auto callback = data_object->PopObject< - std::function<void(int, std::string, std::string)>>(); - - // call callback - callback(exit_code, process_stdout, process_stderr); - }; - - Thread::Task::TaskRunnable runner = - [](GpgFrontend::Thread::Task::DataObjectPtr data_object) -> int { - SPDLOG_DEBUG("process runner called, data object size: {}", - data_object->GetObjectSize()); - - if (data_object->GetObjectSize() != 4) - throw std::runtime_error("invalid data object size"); - - SPDLOG_DEBUG("runner pop object"); - // get arguments - auto cmd = data_object->PopObject<std::string>(); - auto arguments = data_object->PopObject<std::vector<std::string>>(); - auto interact_func = - data_object->PopObject<std::function<void(QProcess *)>>(); - SPDLOG_DEBUG("runner pop object done"); - - auto *cmd_process = new QProcess(); - cmd_process->setProcessChannelMode(QProcess::MergedChannels); - - QObject::connect(cmd_process, &QProcess::started, - []() -> void { SPDLOG_DEBUG("process started"); }); - QObject::connect( - cmd_process, &QProcess::readyReadStandardOutput, - [interact_func, cmd_process]() { interact_func(cmd_process); }); - QObject::connect(cmd_process, &QProcess::errorOccurred, - [=](QProcess::ProcessError error) { - SPDLOG_ERROR("error in executing command: {} error: {}", - cmd, error); - }); - QObject::connect( - cmd_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), - [=](int, QProcess::ExitStatus status) { - if (status == QProcess::NormalExit) - SPDLOG_DEBUG( - "proceess finished, succeed in executing command: {}, exit " - "status: {}", - cmd, status); - else - SPDLOG_ERROR( - "proceess finished, error in executing command: {}, exit " - "status: {}", - cmd, status); - }); - - cmd_process->setProgram(QString::fromStdString(cmd)); - cmd_process->setProcessChannelMode(QProcess::SeparateChannels); - - QStringList q_arguments; - for (const auto &argument : arguments) - q_arguments.append(QString::fromStdString(argument)); - cmd_process->setArguments(q_arguments); - - SPDLOG_DEBUG("process start ready, cmd: {} {}", cmd, - q_arguments.join(" ").toStdString()); - - cmd_process->start(); - cmd_process->waitForFinished(); - - std::string process_stdout = - cmd_process->readAllStandardOutput().toStdString(), - process_stderr = - cmd_process->readAllStandardError().toStdString(); - int exit_code = cmd_process->exitCode(); - - cmd_process->close(); - cmd_process->deleteLater(); - - // transfer result - SPDLOG_DEBUG("runner append object"); - data_object->AppendObject(std::move(process_stderr)); - data_object->AppendObject(std::move(process_stdout)); - data_object->AppendObject(std::move(exit_code)); - SPDLOG_DEBUG("runner append object done"); - - return 0; - }; +void GpgCommandExecutor::ExecuteConcurrentlyAsync(ExecuteContexts contexts) { + for (auto &context : contexts) { + const auto &cmd = context.cmd; + GF_CORE_LOG_INFO("gpg concurrently called cmd {}", cmd); + + Thread::Task *task = BuildTaskFromExecCtx(context); + + if (context.task_runner != nullptr) { + context.task_runner->PostTask(task); + } else { + GpgFrontend::Thread::TaskRunnerGetter::GetInstance() + .GetTaskRunner( + Thread::TaskRunnerGetter::kTaskRunnerType_External_Process) + ->PostTask(task); + } + } +} - // data transfer into task - auto data_object = std::make_shared<Thread::Task::DataObject>(); - data_object->AppendObject(std::move(callback)); - data_object->AppendObject(std::move(interact_func)); - data_object->AppendObject(std::move(arguments)); - data_object->AppendObject(std::move(std::string{cmd})); +void GpgCommandExecutor::ExecuteConcurrentlySync(ExecuteContexts contexts) { + QEventLoop looper; + auto remaining_tasks = contexts.size(); + Thread::TaskRunnerPtr target_task_runner = nullptr; + + for (auto &context : contexts) { + const auto &cmd = context.cmd; + GF_CORE_LOG_DEBUG("gpg concurrently called cmd: {}", cmd); + + Thread::Task *task = BuildTaskFromExecCtx(context); + + QObject::connect(task, &Thread::Task::SignalTaskEnd, [&]() { + --remaining_tasks; + GF_CORE_LOG_DEBUG("remaining tasks: {}", remaining_tasks); + if (remaining_tasks <= 0) { + GF_CORE_LOG_DEBUG("no remaining task, quit"); + looper.quit(); + } + }); + + if (context.task_runner != nullptr) { + target_task_runner = context.task_runner; + } else { + target_task_runner = + GpgFrontend::Thread::TaskRunnerGetter::GetInstance().GetTaskRunner( + Thread::TaskRunnerGetter::kTaskRunnerType_External_Process); + } + + target_task_runner->PostTask(task); + } + + GF_CORE_LOG_TRACE("blocking until concurrent gpg commands finish..."); + // block until task finished + // this is to keep reference vaild until task finished + looper.exec(); +} - auto *process_task = new GpgFrontend::Thread::Task( - std::move(runner), fmt::format("ExecuteConcurrently/{}", cmd), - data_object, std::move(result_callback), false); +GpgCommandExecutor::ExecuteContext::ExecuteContext( + QString cmd, QStringList arguments, GpgCommandExecutorCallback callback, + Module::TaskRunnerPtr task_runner, GpgCommandExecutorInteractor int_func) + : cmd(std::move(cmd)), + arguments(std::move(arguments)), + cb_func(std::move(callback)), + int_func(std::move(int_func)), + task_runner(std::move(task_runner)) {} - GpgFrontend::Thread::TaskRunnerGetter::GetInstance() - .GetTaskRunner(Thread::TaskRunnerGetter::kTaskRunnerType_External_Process) - ->PostTask(process_task); -} +} // namespace GpgFrontend
\ No newline at end of file |