summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <[email protected]>2025-02-23 01:59:00 +0100
committerRobert Griebl <[email protected]>2025-06-05 10:59:59 +0200
commit4b55a7c85a3cfefb128558d6b0cbaaa7e04b8885 (patch)
tree442a29254a6cb1a8a62f81a5f2b22d7575627eab
parent14dca8990d7aaa94cbc0247bd60f5775e0f26d8f (diff)
Remove the long obsolete SoftwareContainers exampleHEADdev
Change-Id: I4ec8eba083503f4318cc896f8e3f571f6234a2a3 Pick-to: 6.10 Reviewed-by: Dominik Holland <[email protected]>
-rw-r--r--doc/container.qdoc13
-rw-r--r--doc/introduction.qdoc3
-rw-r--r--examples/applicationmanager/CMakeLists.txt3
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/CMakeLists.txt32
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/doc/images/softwarecontainer-by-dalle.jpgbin22269 -> 0 bytes
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/doc/src/softwarecontainer-plugin.qdoc86
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json58
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp577
-rw-r--r--examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h109
-rw-r--r--src/plugin-interfaces/containerinterface.cpp4
10 files changed, 9 insertions, 876 deletions
diff --git a/doc/container.qdoc b/doc/container.qdoc
index 78fab64a..1761b1fb 100644
--- a/doc/container.qdoc
+++ b/doc/container.qdoc
@@ -39,7 +39,7 @@ add its full path to the list of plugins to load in the application manager's co
\badcode
plugins:
- container: [ "/full/path/to/softwarecontainers.so", "/another/plugin.so" ]
+ container: [ "/full/path/to/mycontainers.so", "/another/plugin.so" ]
\endcode
Plugins installed into Qt's plugin directory into the \c appman_container folder are picked
@@ -72,7 +72,7 @@ control which container integration is used:
selection:
- com.pelagicore.*: "process"
- com.navigation: "special-container"
- - '*': "softwarecontainers" # a single asterisk needs to be quoted
+ - '*': "mycontainers" # a single asterisk needs to be quoted
\endcode
\li Afterwards, if the System UI did set the ApplicationManager::containerSelectionFunction
@@ -98,9 +98,8 @@ Custom container solutions can be added via plugins. These plugins need not to b
of the application manager, but they need to be built against a private Qt module to get the
interface definition.
-The \l{SoftwareContainer Plugin Example} can be used as a blueprint to either create a
-customer-specific production version of a SoftwareContainer plugin, or to integrate another
-container solution.
+The \l{Bubblewrap Container} plugin can be used as a blueprint to integrate another container
+solution.
Following a brief introduction what steps needs to be done to build your own plugin. First you
need to configure your build system.
@@ -132,12 +131,12 @@ and from \l ContainerManagerInterface respectively:
\code
#include <QtAppManPluginInterfaces/containerinterface.h>
-class SoftwareContainer : public ContainerInterface
+class MyContainer : public ContainerInterface
{
// ...
};
-class SoftwareContainerManager : public QObject, public ContainerManagerInterface
+class MyContainerManager : public QObject, public ContainerManagerInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID AM_ContainerManagerInterface_iid)
diff --git a/doc/introduction.qdoc b/doc/introduction.qdoc
index 7a074444..35f03621 100644
--- a/doc/introduction.qdoc
+++ b/doc/introduction.qdoc
@@ -71,8 +71,7 @@ In addition to the runtime abstraction, the application manager is also able to
inside a \e container instead of just an external Unix process. Support for these container-based
solutions must be provided by the customer and could range from full container solutions like
KVM or XEN to LXC or even down to mandatory access control frameworks like AppArmor or SELinux.
-The application manager includes an example integration of Pelagicore's SoftwareContainers, which
-are based on Linux Containers (LXC).
+The application manager includes an integration with \l{Bubblewrap Container}{bubblewrap containers}.
\target input management
\section1 User Input Management
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
deleted file mode 100644
index c869bc5d..00000000
--- a/examples/applicationmanager/softwarecontainer-plugin/doc/images/softwarecontainer-by-dalle.jpg
+++ /dev/null
Binary files differ
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
diff --git a/src/plugin-interfaces/containerinterface.cpp b/src/plugin-interfaces/containerinterface.cpp
index 6c77ff7f..b17e79df 100644
--- a/src/plugin-interfaces/containerinterface.cpp
+++ b/src/plugin-interfaces/containerinterface.cpp
@@ -367,8 +367,8 @@ bool ContainerManagerInterface::initialize(ContainerHelperFunctions *) { return
This is the interface that you have to implement, if you want to get your own custom container
solution into the application manager.
- For an example, please see \c{examples/software-container}. This example shows the integration
- of Pelagicore's SoftwareContainers (which are essentially nice-to-use LXC wrappers).
+ For an example, please see \c{src/plugins/bubblewrap-container-plugin/}. This is a plugin, that
+ integrates with \c bubblewrap containers.
*/
/*! \fn void ContainerManagerInterface::initialize(ContainerHelperFunctions *helpers)