summaryrefslogtreecommitdiffstats
path: root/examples
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 /examples
parent14dca8990d7aaa94cbc0247bd60f5775e0f26d8f (diff)
Remove the long obsolete SoftwareContainers exampleHEADdev
Change-Id: I4ec8eba083503f4318cc896f8e3f571f6234a2a3 Pick-to: 6.10 Reviewed-by: Dominik Holland <[email protected]>
Diffstat (limited to 'examples')
-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
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
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