diff options
author | Robert Griebl <[email protected]> | 2025-02-23 01:59:00 +0100 |
---|---|---|
committer | Robert Griebl <[email protected]> | 2025-06-05 10:59:59 +0200 |
commit | 4b55a7c85a3cfefb128558d6b0cbaaa7e04b8885 (patch) | |
tree | 442a29254a6cb1a8a62f81a5f2b22d7575627eab /examples | |
parent | 14dca8990d7aaa94cbc0247bd60f5775e0f26d8f (diff) |
Change-Id: I4ec8eba083503f4318cc896f8e3f571f6234a2a3
Pick-to: 6.10
Reviewed-by: Dominik Holland <[email protected]>
Diffstat (limited to 'examples')
7 files changed, 0 insertions, 865 deletions
diff --git a/examples/applicationmanager/CMakeLists.txt b/examples/applicationmanager/CMakeLists.txt index 945afc1e..ee62063f 100644 --- a/examples/applicationmanager/CMakeLists.txt +++ b/examples/applicationmanager/CMakeLists.txt @@ -13,9 +13,6 @@ if (QT_FEATURE_am_package_server) qt_internal_add_example(package-installation) endif() qt_internal_add_example(custom-appman) -if(LINUX) - qt_internal_add_example(softwarecontainer-plugin) -endif() if (QT_FEATURE_am_bubblewrap_container) qt_internal_add_example(bubblewrap-example) endif() diff --git a/examples/applicationmanager/softwarecontainer-plugin/CMakeLists.txt b/examples/applicationmanager/softwarecontainer-plugin/CMakeLists.txt deleted file mode 100644 index 0f6a4972..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(softwarecontainer-plugin LANGUAGES CXX) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/applicationmanager/softwarecontainer-plugin") - - -find_package(Qt6 COMPONENTS Core DBus AppManCommonPrivate AppManPluginInterfacesPrivate) - -qt_standard_project_setup(REQUIRES 6.5) - -qt_add_plugin(softwarecontainer-plugin - softwarecontainer.h - softwarecontainer.cpp - CLASS_NAME softwarecontainer_plugin -) - -target_link_libraries(softwarecontainer-plugin PUBLIC - Qt::Core - Qt::DBus - Qt::AppManCommonPrivate - Qt::AppManPluginInterfacesPrivate -) - -install(TARGETS softwarecontainer-plugin - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" -) diff --git a/examples/applicationmanager/softwarecontainer-plugin/doc/images/softwarecontainer-by-dalle.jpg b/examples/applicationmanager/softwarecontainer-plugin/doc/images/softwarecontainer-by-dalle.jpg Binary files differdeleted file mode 100644 index c869bc5d..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/doc/images/softwarecontainer-by-dalle.jpg +++ /dev/null diff --git a/examples/applicationmanager/softwarecontainer-plugin/doc/src/softwarecontainer-plugin.qdoc b/examples/applicationmanager/softwarecontainer-plugin/doc/src/softwarecontainer-plugin.qdoc deleted file mode 100644 index 8f697940..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/doc/src/softwarecontainer-plugin.qdoc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// Copyright (C) 2019 Luxoft Sweden AB -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - -\example applicationmanager/softwarecontainer-plugin -\brief Learn how to integrate Software Containers with the Application Manager. -\ingroup applicationmanager-examples -\title SoftwareContainer Plugin Example -\image softwarecontainer-by-dalle.jpg SoftwareContainer Plugin Example - -\note Please \l{wayland-dev-packages}{read this} if you want to build the example on a Linux machine. - -\section1 Introduction - -This example shows how to integrate \l{https://github.com/Pelagicore/softwarecontainer} -{Pelagicore's SoftwareContainers} with the Application Manager. - -\note As a prerequisite, familiarize yourself with \l{Containers} beforehand. - -In \c softwarecontainer.cpp, parts of the container's configuration is hardcoded; all of the -capability definition is in the JSON manifest file located in -\c{service-manifest.d/io.qt.AppliciationManager.Application/}. - -The Wayland/OpenGL passthrough is tested against Intel GPUs and VMWare's virtual GPU. - -\section1 Run the SoftwareContainer Agent - -To run the softwarecontainer-agent, you must start it as root. By default, this agent registers -itself on the system D-Bus. So, you need to have a policy file in place, to allow the agent to -register itself on the system-bus. - -If you want to run the agent on the session bus instead, via the \c{--session-bus} parameter, -then you have to add the following lines to one of your \c config.yaml files: - -\badcode -containers: - softwarecontainer: - dbus: 'session' -\endcode - -It's mandatory to pass the service manifest directory that comes with the plugin via \c{-m}. -Otherwise, the container setup fails due to the missing \c{io.qt.ApplicationManager.Application} -capability. - -Make sure that the agent has access to the same session bus that the application manager uses, -if you intend on forwarding this bus. If the agent is run as root, but the application manager -isn't, this can be tricky -- since the default session bus policy in most Linux distros -disallows root to access user session busses. However, you can workaround this issue by adding -an \c{<allow user="root"/>} policy within the \c{<policy context="default">} element in -\c{/etc/dbus-1/session.conf}. - -Additionally, make sure to tell the agent about your environment, when running it via sudo: - -\badcode -sudo XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR -DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS softwarecontainer-agent -m -/path/to/application-manager/examples/softwarecontainer-plugin/service-manifest.d/ -\endcode - -On the Application Manager side, you need to activate the plugin by adding a line similar to the -one shown below, to one of your \c config.yaml files: - -\badcode -plugins: - container: [ "/path/to/libsoftwarecontainer-plugin.so" ] -\endcode - -To actually run applications within software containers, you have to add a container selection -configuration. For more information, see \l{Container Selection Configuration}. - -To simplify developing on the desktop, normally, you want your \c $HOME directory mounted into -the container in \c read-only mode. This way, you don't have to install your application manager -into \c{/usr/} after every build. This behavior only works, given that your build directory is -located somewhere in \c{$HOME}, otherwise the container won't see the appman-launcher-qml binary. -This behavior is \b not enabled by default; but you can activate it with the following lines in -one of your \c config.yaml files: - -\badcode -containers: - softwarecontainer: - bindMountHome: yes -\endcode - -*/ diff --git a/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json b/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json deleted file mode 100644 index 68592c08..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "version": "1", - "capabilities": [ - { - "name": "io.qt.ApplicationManager.Application", - "gateways": [ - { - "id": "dbus", - "config": [ - { - "dbus-gateway-config-session": [ - { - "direction": "*", - "interface": "*", - "object-path": "*", - "method": "*" - } - ], - "dbus-gateway-config-system": [ - { - "direction": "*", - "interface": "*", - "object-path": "*", - "method": "*" - } - ] - } - ] - }, - { - "id": "wayland", - "config": [ - { - "enabled": true - } - ] - }, - { - "id": "devicenode", - "config": [ - { - "name": "/dev/dri/card0" - }, - { - "name": "/dev/dri/renderD128" - }, - { - "name": "/dev/tty0" - }, - { - "name": "/dev/tty1" - } - ] - } - ] - } - ] -} diff --git a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp deleted file mode 100644 index c09b8b2d..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// Copyright (C) 2019 Luxoft Sweden AB -// Copyright (C) 2018 Pelagicore AG -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -#include <tuple> - -#include <QtDBus/QtDBus> -#include <QJsonDocument> -#include <QSocketNotifier> -#include <QMetaObject> -#include <qplatformdefs.h> -#include <unistd.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/fcntl.h> -#include "softwarecontainer.h" - - -QT_BEGIN_NAMESPACE - -QDBusArgument &operator<<(QDBusArgument &argument, const QMap<QString,QString> &map) -{ - argument.beginMap(QMetaType::QString, QMetaType::QString); - for (auto it = map.cbegin(); it != map.cend(); ++it) { - argument.beginMapEntry(); - argument << it.key() << it.value(); - argument.endMapEntry(); - } - argument.endMap(); - - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString,QString> &map) -{ - argument.beginMap(); - while (!argument.atEnd()) { - argument.beginMapEntry(); - QString key, value; - argument >> key >> value; - map.insert(key, value); - argument.endMapEntry(); - } - argument.endMap(); - - return argument; -} - -QT_END_NAMESPACE - - - -// unfortunately, this is a copy of the code from debugwrapper.cpp -static QStringList substituteCommand(const QStringList &debugWrapperCommand, const QString &program, - const QStringList &arguments) -{ - QString stringifiedArguments = arguments.join(QLatin1Char(' ')); - QStringList result; - - for (const QString &s : debugWrapperCommand) { - if (s == QStringLiteral("%arguments%")) { - result << arguments; - } else { - QString str(s); - str.replace(QStringLiteral("%program%"), program); - str.replace(QStringLiteral("%arguments%"), stringifiedArguments); - result << str; - } - } - return result; -} - -SoftwareContainerManager::SoftwareContainerManager() -{ - static bool once = false; - if (!once) { - once = true; - qDBusRegisterMetaType<QMap<QString, QString>>(); - } -} - -QString SoftwareContainerManager::identifier() const -{ - return QStringLiteral("softwarecontainer"); -} - -bool SoftwareContainerManager::supportsQuickLaunch() const -{ - return false; -} - -void SoftwareContainerManager::setConfiguration(const QVariantMap &configuration) -{ - m_configuration = configuration; -} - -ContainerInterface *SoftwareContainerManager::create(bool isQuickLaunch, const QVector<int> &stdioRedirections, - const QMap<QString, QString> &debugWrapperEnvironment, - const QStringList &debugWrapperCommand) -{ - if (!m_interface) { - QString dbus = configuration().value(QStringLiteral("dbus")).toString(); - QDBusConnection conn(QStringLiteral("sc-bus")); - - if (dbus.isEmpty()) - dbus = QStringLiteral("system"); - - if (dbus == QLatin1String("system")) - conn = QDBusConnection::systemBus(); - else if (dbus == QLatin1String("session")) - conn = QDBusConnection::sessionBus(); - else - conn = QDBusConnection::connectToBus(dbus, QStringLiteral("sc-bus")); - - if (!conn.isConnected()) { - qWarning() << "The" << dbus << "D-Bus is not available to connect to the SoftwareContainer agent."; - return nullptr; - } - m_interface = new QDBusInterface(QStringLiteral("com.pelagicore.SoftwareContainerAgent"), - QStringLiteral("/com/pelagicore/SoftwareContainerAgent"), - QStringLiteral("com.pelagicore.SoftwareContainerAgent"), - conn, this); - if (m_interface->lastError().isValid()) { - qWarning() << "Could not connect to com.pelagicore.SoftwareContainerAgent, " - "/com/pelagicore/SoftwareContainerAgent on the" << dbus << "D-Bus"; - delete m_interface; - m_interface = nullptr; - return nullptr; - } - - if (!connect(m_interface, SIGNAL(ProcessStateChanged(int,uint,bool,uint)), this, SLOT(processStateChanged(int,uint,bool,uint)))) { - qWarning() << "Could not connect to the com.pelagicore.SoftwareContainerAgent.ProcessStateChanged " - "signal on the" << dbus << "D-Bus"; - delete m_interface; - m_interface = nullptr; - return nullptr; - } - } - - QString config = QStringLiteral("[]"); - QVariant v = configuration().value(QStringLiteral("createConfig")); - if (v.isValid()) - config = QString::fromUtf8(QJsonDocument::fromVariant(v).toJson(QJsonDocument::Compact)); - - QDBusMessage reply = m_interface->call(QDBus::Block, QStringLiteral("Create"), config); - if (reply.type() == QDBusMessage::ErrorMessage) { - qWarning() << "SoftwareContainer failed to create a new container:" << reply.errorMessage() - << "(config was:" << config << ")"; - return nullptr; - } - - int containerId = reply.arguments().at(0).toInt(); - - if (containerId < 0) { - qCritical() << "SoftwareContainer failed to create a new container. (config was:" << config << ")"; - return nullptr; - } - - // calculate where to dump stdout/stderr - // also close all file descriptors that we do not need - int inFd = stdioRedirections.value(STDIN_FILENO, -1); - int outFd = stdioRedirections.value(STDOUT_FILENO, -1); - int errFd = stdioRedirections.value(STDERR_FILENO, -1); - int outputFd = STDOUT_FILENO; - - if (inFd >= 0) - ::close(inFd); - if (errFd >= 0) - outputFd = errFd; - if (outFd >= 0) { - if (errFd < 0) - outputFd = outFd; - else - ::close(outFd); - } - - SoftwareContainer *container = new SoftwareContainer(this, isQuickLaunch, containerId, - outputFd, debugWrapperEnvironment, - debugWrapperCommand); - m_containers.insert(containerId, container); - connect(container, &QObject::destroyed, this, [this, containerId]() { m_containers.remove(containerId); }); - return container; -} - -QDBusInterface *SoftwareContainerManager::interface() const -{ - return m_interface; -} - -QVariantMap SoftwareContainerManager::configuration() const -{ - return m_configuration; -} - -void SoftwareContainerManager::processStateChanged(int containerId, uint processId, bool isRunning, uint exitCode) -{ - Q_UNUSED(processId) - - SoftwareContainer *container = m_containers.value(containerId); - if (!container) { - qWarning() << "Received a processStateChanged signal for unknown container" << containerId; - return; - } - - if (!isRunning) - container->containerExited(exitCode); -} - - - -SoftwareContainer::SoftwareContainer(SoftwareContainerManager *manager, bool isQuickLaunch, int containerId, - int outputFd, const QMap<QString, QString> &debugWrapperEnvironment, - const QStringList &debugWrapperCommand) - : m_manager(manager) - , m_isQuickLaunch(isQuickLaunch) - , m_id(containerId) - , m_outputFd(outputFd) - , m_debugWrapperEnvironment(debugWrapperEnvironment) - , m_debugWrapperCommand(debugWrapperCommand) -{ } - -SoftwareContainer::~SoftwareContainer() -{ - if (m_fifoFd >= 0) - QT_CLOSE(m_fifoFd); - if (!m_fifoPath.isEmpty()) - ::unlink(m_fifoPath); - if (m_outputFd > STDERR_FILENO) - QT_CLOSE(m_outputFd); -} - -SoftwareContainerManager *SoftwareContainer::manager() const -{ - return m_manager; -} - -bool SoftwareContainer::attachApplication(const QVariantMap &application) -{ - - // In normal launch attachApplication is called first, then the start() - // method is called. During quicklaunch start() is called first and then - // attachApplication. In this case we need to configure the container - // with any extra capabilities etc. - - m_state = StartingUp; - m_application = application; - - m_hostPath = application.value(QStringLiteral("codeDir")).toString(); - if (m_hostPath.isEmpty()) - m_hostPath = QDir::currentPath(); - - m_appRelativeCodePath = application.value(QStringLiteral("codeFilePath")).toString(); - m_containerPath = QStringLiteral("/app"); - - // If this is a quick launch instance, we need to renew the capabilities - // and send the bindmounts. - if (m_isQuickLaunch) { - if (!sendCapabilities()) - return false; - - if (!sendBindMounts()) - return false; - } - - m_ready = true; - emit ready(); - return true; -} - -QString SoftwareContainer::controlGroup() const -{ - return QString(); -} - -bool SoftwareContainer::setControlGroup(const QString &groupName) -{ - Q_UNUSED(groupName) - return false; -} - -bool SoftwareContainer::setProgram(const QString &program) -{ - m_program = program; - return true; -} - -void SoftwareContainer::setBaseDirectory(const QString &baseDirectory) -{ - m_baseDir = baseDirectory; -} - -bool SoftwareContainer::isReady() const -{ - return m_ready; -} - -QString SoftwareContainer::mapContainerPathToHost(const QString &containerPath) const -{ - return containerPath; -} - -QString SoftwareContainer::mapHostPathToContainer(const QString &hostPath) const -{ - QString containerPath = m_containerPath; - - QFileInfo fileInfo(hostPath); - if (fileInfo.isFile()) - containerPath = m_containerPath + QStringLiteral("/") + fileInfo.fileName(); - - return containerPath; -} - -bool SoftwareContainer::sendCapabilities() -{ - auto iface = manager()->interface(); - if (!iface) - return false; - - // this is the one and only capability in io.qt.ApplicationManager.Application.json - static const QStringList capabilities { QStringLiteral("io.qt.ApplicationManager.Application") }; - - QDBusMessage reply = iface->call(QDBus::Block, QStringLiteral("SetCapabilities"), m_id, QVariant::fromValue(capabilities)); - if (reply.type() == QDBusMessage::ErrorMessage) { - qWarning() << "SoftwareContainer failed to set capabilities to" << capabilities << ":" << reply.errorMessage(); - return false; - } - return true; -} - -bool SoftwareContainer::sendBindMounts() -{ - auto iface = manager()->interface(); - if (!iface) - return false; - - QFileInfo fontCacheInfo(QStringLiteral("/var/cache/fontconfig")); - - QVector<std::tuple<QString, QString, bool>> bindMounts; // bool == isReadOnly - // the private P2P D-Bus - bindMounts.append(std::make_tuple(m_dbusP2PInfo.absoluteFilePath(), m_dbusP2PInfo.absoluteFilePath(), false)); - - // we need to share the fontconfig cache - otherwise the container startup might take a long time - bindMounts.append(std::make_tuple(fontCacheInfo.absoluteFilePath(), fontCacheInfo.absoluteFilePath(), false)); - - // Qt's plugin path - bindMounts.append(std::make_tuple(m_qtPluginPathInfo.absoluteFilePath(), m_qtPluginPathInfo.absoluteFilePath(), false)); - - // the actual path to the application - bindMounts.append(std::make_tuple(m_hostPath, m_containerPath, true)); - - // for development only - mount the user's $HOME dir into the container as read-only. Otherwise - // you would have to `make install` the AM into /usr on every rebuild - if (manager()->configuration().value(QStringLiteral("bindMountHome")).toBool()) - bindMounts.append(std::make_tuple(QDir::homePath(), QDir::homePath(), true)); - - // do all the bind-mounts in parallel to waste as little time as possible - QList<QDBusPendingReply<>> bindMountResults; - - for (auto it = bindMounts.cbegin(); it != bindMounts.cend(); ++it) - bindMountResults << iface->asyncCall(QStringLiteral("BindMount"), m_id, std::get<0>(*it), - std::get<1>(*it), std::get<2>(*it)); - - for (const auto &pending : std::as_const(bindMountResults)) - QDBusPendingCallWatcher(pending).waitForFinished(); - - for (int i = 0; i < bindMounts.size(); ++i) { - if (bindMountResults.at(i).isError()) { - qWarning() << "SoftwareContainer failed to bind-mount the directory" << std::get<0>(bindMounts.at(i)) - << "into the container at" << std::get<1>(bindMounts.at(i)) << ":" << bindMountResults.at(i).error().message(); - return false; - } - } - - return true; - -} - -bool SoftwareContainer::start(const QStringList &arguments, const QMap<QString, QString> &runtimeEnvironment, - const QVariantMap &amConfig) -{ - auto iface = manager()->interface(); - if (!iface) - return false; - - if (!QFile::exists(m_program)) - return false; - - // Send initial capabilities even if this is a quick-launch instance - if (!sendCapabilities()) - return false; - - // parse out the actual socket file name from the DBus specification - QString dbusP2PSocket = amConfig.value(QStringLiteral("dbus")).toMap().value(QStringLiteral("p2p")).toString(); - dbusP2PSocket = dbusP2PSocket.mid(dbusP2PSocket.indexOf(QLatin1Char('=')) + 1); - dbusP2PSocket = dbusP2PSocket.left(dbusP2PSocket.indexOf(QLatin1Char(','))); - QFileInfo dbusP2PInfo(dbusP2PSocket); - m_dbusP2PInfo = dbusP2PInfo; - - // for bind-mounting the plugin-path later on - QString qtPluginPath = runtimeEnvironment.value(QStringLiteral("QT_PLUGIN_PATH")); - m_qtPluginPathInfo = QFileInfo(qtPluginPath); - - // Only send the bindmounts if this is not a quick-launch instance. - if (!m_isQuickLaunch && !sendBindMounts()) - return false; - - // create an unique fifo name in /tmp - m_fifoPath = QDir::tempPath().toLocal8Bit() + "/sc-" + QUuid::createUuid().toByteArray().mid(1,36) + ".fifo"; - if (::mkfifo(m_fifoPath, 0600) != 0) { - qWarning() << "Failed to create FIFO at" << m_fifoPath << "to redirect stdout/stderr out of the container:" << strerror(errno); - m_fifoPath.clear(); - return false; - } - - // open fifo for reading - // due to QTBUG-15261 (bytesAvailable() on a fifo always returns 0) we use a raw fd - m_fifoFd = ::open(m_fifoPath, O_RDONLY | O_NONBLOCK); - if (m_fifoFd < 0) { - qWarning() << "Failed to open FIFO at" << m_fifoPath << "for reading:" << strerror(errno); - return false; - } - - // read from fifo and dump to message handler - QSocketNotifier *sn = new QSocketNotifier(m_fifoFd, QSocketNotifier::Read, this); - int outputFd = m_outputFd; - connect(sn, &QSocketNotifier::activated, this, [sn, outputFd](int fifoFd) { - int bytesAvailable = 0; - if (ioctl(fifoFd, FIONREAD, &bytesAvailable) == 0) { - static const int bufferSize = 4096; - static QByteArray buffer(bufferSize, 0); - - while (bytesAvailable > 0) { - auto bytesRead = QT_READ(fifoFd, buffer.data(), std::min(bytesAvailable, bufferSize)); - if (bytesRead < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - sn->setEnabled(false); - return; - } else if (bytesRead > 0) { - (void) QT_WRITE(outputFd, buffer.constData(), bytesRead); - bytesAvailable -= bytesRead; - } - } - } - }); - - // Calculate the exact command to run - QStringList command; - if (!m_debugWrapperCommand.isEmpty()) { - command = substituteCommand(m_debugWrapperCommand, m_program, arguments); - } else { - command = arguments; - command.prepend(m_program); - } - - // SC expects a plain string instead of individual args - QString cmdLine; - for (const auto &part : std::as_const(command)) { - if (!cmdLine.isEmpty()) - cmdLine.append(QLatin1Char(' ')); - cmdLine.append(QLatin1Char('\"')); - cmdLine.append(part); - cmdLine.append(QLatin1Char('\"')); - } - //cmdLine.prepend(QStringLiteral("/usr/bin/strace ")); // useful if things go wrong... - - // we start with a copy of the AM's environment, but some variables would overwrite important - // redirections set by SC gateways. - static const QStringList forbiddenVars = { - QStringLiteral("XDG_RUNTIME_DIR"), - QStringLiteral("DBUS_SESSION_BUS_ADDRESS"), - QStringLiteral("DBUS_SYSTEM_BUS_ADDRESS"), - QStringLiteral("PULSE_SERVER") - }; - - // since we have to translate between a QProcessEnvironment and a QMap<>, we cache the result - static QMap<QString, QString> baseEnvVars; - if (baseEnvVars.isEmpty()) { - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - const auto keys = env.keys(); - for (const auto &key : keys) { - if (!key.isEmpty() && !forbiddenVars.contains(key)) - baseEnvVars.insert(key, env.value(key)); - } - } - - QMap<QString, QString> envVars = baseEnvVars; - - // set the env. variables coming from the runtime - for (auto it = runtimeEnvironment.cbegin(); it != runtimeEnvironment.cend(); ++it) { - if (it.value().isEmpty()) - envVars.remove(it.key()); - else - envVars.insert(it.key(), it.value()); - } - // set the env. variables coming from a debug wrapper - for (auto it = m_debugWrapperEnvironment.cbegin(); it != m_debugWrapperEnvironment.cend(); ++it) { - if (it.value().isEmpty()) - envVars.remove(it.key()); - else - envVars.insert(it.key(), it.value()); - } - - QVariant venvVars = QVariant::fromValue(envVars); - - qDebug () << "SoftwareContainer is trying to launch application" << m_id - << "\n * command ..." << cmdLine - << "\n * directory ." << m_containerPath - << "\n * output ...." << m_fifoPath - << "\n * environment" << envVars; - -#if 0 - qWarning() << "Attach to container now!"; - qWarning().nospace() << " sudo lxc-attach -n SC-" << m_id; - sleep(10000000); -#endif - - auto reply = iface->call(QDBus::Block, QStringLiteral("Execute"), m_id, cmdLine, - m_containerPath, QString::fromLocal8Bit(m_fifoPath), venvVars); - if (reply.type() == QDBusMessage::ErrorMessage) { - qWarning() << "SoftwareContainer failed to execute application" << m_id << "in directory" - << m_containerPath << "in the container:" << reply.errorMessage(); - return false; - } - - m_pid = reply.arguments().at(0).value<int>(); - - m_state = Running; - QMetaObject::invokeMethod(this, [this]() { - emit stateChanged(m_state); - emit started(); - }, Qt::QueuedConnection); - return true; -} - -qint64 SoftwareContainer::processId() const -{ - return m_pid; -} - -SoftwareContainer::RunState SoftwareContainer::state() const -{ - return m_state; -} - -void SoftwareContainer::kill() -{ - auto iface = manager()->interface(); - - if (iface) { - QDBusMessage reply = iface->call(QDBus::Block, QStringLiteral("Destroy"), m_id); - if (reply.type() == QDBusMessage::ErrorMessage) { - qWarning() << "SoftwareContainer failed to destroy container" << reply.errorMessage(); - } - - if (!reply.arguments().at(0).toBool()) { - qWarning() << "SoftwareContainer failed to destroy container."; - } - } -} - -void SoftwareContainer::terminate() -{ - kill(); -} - -void SoftwareContainer::containerExited(uint exitCode) -{ - m_state = NotRunning; - emit stateChanged(m_state); - emit finished(WEXITSTATUS(exitCode), WIFEXITED(exitCode) ? NormalExit : CrashExit); - deleteLater(); -} - -#include "moc_softwarecontainer.cpp" diff --git a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h deleted file mode 100644 index 4ed83419..00000000 --- a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// Copyright (C) 2019 Luxoft Sweden AB -// Copyright (C) 2018 Pelagicore AG -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -#ifndef SOFTWARECONTAINER_H -#define SOFTWARECONTAINER_H - -#include <QVariantMap> -#include <QFileInfo> - -#include <QtAppManPluginInterfaces/containerinterface.h> - -QT_FORWARD_DECLARE_CLASS(QDBusInterface) - -class SoftwareContainerManager; - -class SoftwareContainer : public ContainerInterface -{ - Q_OBJECT - -public: - SoftwareContainer(SoftwareContainerManager *manager, bool isQuickLaunch, int containerId, - int outputFd, const QMap<QString, QString> &debugWrapperEnvironment, - const QStringList &debugWrapperCommand); - ~SoftwareContainer(); - - SoftwareContainerManager *manager() const; - - bool attachApplication(const QVariantMap &application) override; - - QString controlGroup() const override; - bool setControlGroup(const QString &groupName) override; - - bool setProgram(const QString &program) override; - void setBaseDirectory(const QString &baseDirectory) override; - - bool isReady() const override; - - QString mapContainerPathToHost(const QString &containerPath) const override; - QString mapHostPathToContainer(const QString &hostPath) const override; - - bool start(const QStringList &arguments, const QMap<QString, QString> &runtimeEnvironment, - const QVariantMap &amConfig) override; - - qint64 processId() const override; - RunState state() const override; - - void kill() override; - void terminate() override; - - void containerExited(uint exitCode); - -private: - bool sendCapabilities(); - bool sendBindMounts(); - - SoftwareContainerManager *m_manager; - bool m_isQuickLaunch; - int m_id; - QString m_program; - QString m_baseDir; - bool m_ready = false; - qint64 m_pid = 0; - RunState m_state = NotRunning; - QVariantMap m_application; - QString m_appRelativeCodePath; - QString m_hostPath; - QString m_containerPath; - QByteArray m_fifoPath; - int m_fifoFd = -1; - int m_outputFd; - QMap<QString, QString> m_debugWrapperEnvironment; - QStringList m_debugWrapperCommand; - QFileInfo m_dbusP2PInfo; - QFileInfo m_qtPluginPathInfo; -}; - -class SoftwareContainerManager : public QObject, public ContainerManagerInterface -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID AM_ContainerManagerInterface_iid) - Q_INTERFACES(ContainerManagerInterface) - -public: - SoftwareContainerManager(); - - QString identifier() const override; - bool supportsQuickLaunch() const override; - void setConfiguration(const QVariantMap &configuration) override; - - ContainerInterface *create(bool isQuickLaunch, - const QVector<int> &stdioRedirections, - const QMap<QString, QString> &debugWrapperEnvironment, - const QStringList &debugWrapperCommand) override; -public: - QDBusInterface *interface() const; - QVariantMap configuration() const; - -private Q_SLOTS: - void processStateChanged(int containerId, uint processId, bool isRunning, uint exitCode); - -private: - QVariantMap m_configuration; - QDBusInterface *m_interface = nullptr; - QMap<int, SoftwareContainer *> m_containers; -}; - -#endif // SOFTWARECONTAINER_H |