aboutsummaryrefslogtreecommitdiffstats
path: root/src/core/function/gpg/GpgCommandExecutor.cpp
diff options
context:
space:
mode:
authorSaturn&Eric <[email protected]>2024-01-23 07:21:28 +0000
committerGitHub <[email protected]>2024-01-23 07:21:28 +0000
commit56acf161d439ce73eceaa145c40fe703bb2c3f02 (patch)
treea5d4790a6b2efc8786a3c0f74a07f5a8032d4e94 /src/core/function/gpg/GpgCommandExecutor.cpp
parentfix: use more secure cdn links (diff)
parentfix: solve issues on detected gnupg path function and fix linking errors on w... (diff)
downloadGpgFrontend-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.cpp369
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