diff --git a/api/schema/v1.json b/api/schema/v1.json index 99362b75a..11116f38d 100644 --- a/api/schema/v1.json +++ b/api/schema/v1.json @@ -1458,6 +1458,9 @@ "disable_xdp": { "type": "boolean" }, + "enable_pipewire": { + "type": "boolean" + }, "mounts": { "type": "array", "items": { diff --git a/api/schema/v1.yaml b/api/schema/v1.yaml index 6c87f3f55..ca3e0593c 100644 --- a/api/schema/v1.yaml +++ b/api/schema/v1.yaml @@ -1103,6 +1103,8 @@ $defs: $ref: '#/$defs/DeviceOption' disable_xdp: type: boolean + enable_pipewire: + type: boolean mounts: type: array items: diff --git a/apps/ll-cli/src/main.cpp b/apps/ll-cli/src/main.cpp index 721e1884c..b9c5aab6c 100644 --- a/apps/ll-cli/src/main.cpp +++ b/apps/ll-cli/src/main.cpp @@ -179,6 +179,11 @@ ll-cli run org.deepin.demo -- bash -x /path/to/bash/script)")); runOptions.disableXdp, _("Enable or disable xdg-desktop-portal related integration inside the sandbox")) ->take_last(); + cliRun + ->add_flag("--enable-pipewire", + runOptions.enablePipewireSocketMount, + _("Enable PipeWire socket mount inside the sandbox")) + ->take_last(); cliRun->add_option("--run-context", runOptions.runContext, _("Run context json string")) ->group(""); cliRun diff --git a/libs/api/src/linglong/api/types/v1/Generators.hpp b/libs/api/src/linglong/api/types/v1/Generators.hpp index 0fa78622d..c68e167b3 100644 --- a/libs/api/src/linglong/api/types/v1/Generators.hpp +++ b/libs/api/src/linglong/api/types/v1/Generators.hpp @@ -1313,6 +1313,7 @@ inline void from_json(const json & j, RuntimeConfigure& x) { x.deviceMode = get_stack_optional>(j, "device_mode"); x.devices = get_stack_optional>(j, "devices"); x.disableXdp = get_stack_optional(j, "disable_xdp"); +x.enablePipewireSocketMount = get_stack_optional(j, "enable_pipewire"); x.env = get_stack_optional>(j, "env"); x.extDefs = get_stack_optional>>(j, "ext_defs"); x.instances = get_stack_optional>(j, "instances"); @@ -1330,6 +1331,9 @@ j["devices"] = x.devices; if (x.disableXdp) { j["disable_xdp"] = x.disableXdp; } +if (x.enablePipewireSocketMount) { +j["enable_pipewire"] = x.enablePipewireSocketMount; +} if (x.env) { j["env"] = x.env; } diff --git a/libs/api/src/linglong/api/types/v1/RuntimeConfigure.hpp b/libs/api/src/linglong/api/types/v1/RuntimeConfigure.hpp index 5914a9fb2..f3341b64d 100644 --- a/libs/api/src/linglong/api/types/v1/RuntimeConfigure.hpp +++ b/libs/api/src/linglong/api/types/v1/RuntimeConfigure.hpp @@ -40,6 +40,7 @@ struct RuntimeConfigure { std::optional> deviceMode; std::optional> devices; std::optional disableXdp; +std::optional enablePipewireSocketMount; std::optional> env; /** * external extension definitions to extend the component diff --git a/libs/linglong/src/linglong/cli/cli.h b/libs/linglong/src/linglong/cli/cli.h index b9f7bcebb..d235a85bd 100644 --- a/libs/linglong/src/linglong/cli/cli.h +++ b/libs/linglong/src/linglong/cli/cli.h @@ -58,6 +58,7 @@ struct RunOptions std::optional workdir; std::vector extensions; std::optional disableXdp; + std::optional enablePipewireSocketMount; bool privileged{ false }; std::vector capsAdd; std::vector cdiSpecDir = { "/etc/cdi", "/var/run/cdi" }; diff --git a/libs/linglong/src/linglong/runtime/container_builder.cpp b/libs/linglong/src/linglong/runtime/container_builder.cpp index 4d2953044..edb58391e 100644 --- a/libs/linglong/src/linglong/runtime/container_builder.cpp +++ b/libs/linglong/src/linglong/runtime/container_builder.cpp @@ -9,6 +9,7 @@ #include "linglong/cli/cli.h" #include "linglong/common/dir.h" #include "linglong/common/strings.h" +#include "linglong/common/xdg.h" #include "linglong/oci-cfg-generators/container_cfg_builder.h" #include "linglong/package/architecture.h" #include "linglong/runtime/run_context.h" @@ -130,6 +131,10 @@ auto RunContainerOptions::applyRuntimeConfig( this->disableXdp = *runtimeConfig.disableXdp; } + if (runtimeConfig.enablePipewireSocketMount.has_value()) { + this->enablePipewireSocketMount = *runtimeConfig.enablePipewireSocketMount; + } + if (runtimeConfig.deviceMode) { for (const auto &option : *runtimeConfig.deviceMode) { if (option == api::types::v1::DeviceOption::Passthru) { @@ -172,6 +177,10 @@ auto RunContainerOptions::applyCliRunOptions(const cli::RunOptions &options) noe if (options.disableXdp.has_value()) { this->disableXdp = *options.disableXdp; } + + if (options.enablePipewireSocketMount.has_value()) { + this->enablePipewireSocketMount = *options.enablePipewireSocketMount; + } this->privileged = options.privileged; this->capabilities.insert(this->capabilities.end(), options.capsAdd.begin(), @@ -211,6 +220,11 @@ auto RunContainerOptions::isDevicePassthruEnabled() const noexcept -> bool return this->devicePassthru; } +auto RunContainerOptions::isPipewireSocketMountEnabled() const noexcept -> bool +{ + return this->enablePipewireSocketMount; +} + auto RunContainerOptions::isXdpDisabled() const noexcept -> bool { return this->disableXdp; @@ -590,6 +604,11 @@ auto ContainerBuilder::configureRunContainer(PreparedContainer &prepared, docMountPoint.error()); } } + if (options.isPipewireSocketMountEnabled()) { + auto pwSocketPath = common::xdg::getXDGRuntimeDir() / "pipewire-0"; + prepared.cfgBuilder.enablePipewireSocketMount( + generator::PipewireMountOption{ .hostSocketPath = std::move(pwSocketPath) }); + } if (options.isDevicePassthruEnabled()) { prepared.cfgBuilder.bindDev(true); diff --git a/libs/linglong/src/linglong/runtime/container_builder.h b/libs/linglong/src/linglong/runtime/container_builder.h index 77c2f454a..a0c32c0f7 100644 --- a/libs/linglong/src/linglong/runtime/container_builder.h +++ b/libs/linglong/src/linglong/runtime/container_builder.h @@ -54,11 +54,13 @@ struct RunContainerOptions -> const std::vector &; [[nodiscard]] auto isDevicePassthruEnabled() const noexcept -> bool; [[nodiscard]] auto isXdpDisabled() const noexcept -> bool; + [[nodiscard]] auto isPipewireSocketMountEnabled() const noexcept -> bool; [[nodiscard]] auto isPrivileged() const noexcept -> bool; CommonContainerOptions common; bool disableXdp{ false }; + bool enablePipewireSocketMount{ false }; bool privileged{ false }; bool devicePassthru{ false }; std::map env; diff --git a/libs/linglong/tests/ll-tests/src/linglong/runtime/container_builder_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/runtime/container_builder_test.cpp index f702e923e..8559b8c67 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/runtime/container_builder_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/runtime/container_builder_test.cpp @@ -80,3 +80,51 @@ TEST(RunContainerOptionsTest, ApplyCliRunOptionsOverridesRuntimeConfigXdpSetting ASSERT_TRUE(cliResult); EXPECT_TRUE(options.isXdpDisabled()); } + +TEST(RunContainerOptionsTest, ApplyRuntimeConfigEnablesPipewireSocketMount) +{ + runtime::RunContainerOptions options; + + api::types::v1::RuntimeConfigure runtimeConfig; + runtimeConfig.enablePipewireSocketMount = true; + + auto result = options.applyRuntimeConfig(runtimeConfig); + ASSERT_TRUE(result); + EXPECT_TRUE(options.isPipewireSocketMountEnabled()); +} + +TEST(RunContainerOptionsTest, ApplyCliRunOptionsPreservesRuntimeConfigPipewireSettingByDefault) +{ + runtime::RunContainerOptions options; + + api::types::v1::RuntimeConfigure runtimeConfig; + runtimeConfig.enablePipewireSocketMount = true; + + auto runtimeResult = options.applyRuntimeConfig(runtimeConfig); + ASSERT_TRUE(runtimeResult); + ASSERT_TRUE(options.isPipewireSocketMountEnabled()); + + cli::RunOptions runOptions; + auto cliResult = options.applyCliRunOptions(runOptions); + ASSERT_TRUE(cliResult); + EXPECT_TRUE(options.isPipewireSocketMountEnabled()); +} + +TEST(RunContainerOptionsTest, ApplyCliRunOptionsOverridesRuntimeConfigPipewireSettingWhenSpecified) +{ + runtime::RunContainerOptions options; + + api::types::v1::RuntimeConfigure runtimeConfig; + runtimeConfig.enablePipewireSocketMount = false; + + auto runtimeResult = options.applyRuntimeConfig(runtimeConfig); + ASSERT_TRUE(runtimeResult); + ASSERT_FALSE(options.isPipewireSocketMountEnabled()); + + cli::RunOptions runOptions; + runOptions.enablePipewireSocketMount = true; + + auto cliResult = options.applyCliRunOptions(runOptions); + ASSERT_TRUE(cliResult); + EXPECT_TRUE(options.isPipewireSocketMountEnabled()); +} diff --git a/libs/linglong/tests/ll-tests/src/linglong/utils/runtime_config_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/utils/runtime_config_test.cpp index 223143454..2dcff6b2b 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/utils/runtime_config_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/utils/runtime_config_test.cpp @@ -28,6 +28,7 @@ TEST(RuntimeConfigTest, LoadFromPath) RuntimeConfigure config; config.disableXdp = true; + config.enablePipewireSocketMount = true; config.deviceMode = std::vector{ DeviceOption::Passthru }; config.env = std::map{ { "PATH", "/usr/bin" }, { "HOME", "/home/user" } }; @@ -84,6 +85,7 @@ TEST(RuntimeConfigTest, MergeConfigs) RuntimeConfigure config1; config1.disableXdp = false; config1.deviceMode = std::vector{ DeviceOption::Passthru }; + config1.enablePipewireSocketMount = false; config1.devices = std::vector{ "vendor.com/device=gpu0" }; config1.env = std::map{ { "PATH", "/usr/bin" }, { "HOME", "/home/user1" } }; @@ -98,6 +100,7 @@ TEST(RuntimeConfigTest, MergeConfigs) RuntimeConfigure config2; config2.disableXdp = true; config2.deviceMode = std::vector{ DeviceOption::Passthru }; + config2.enablePipewireSocketMount = true; config2.devices = std::vector{ "vendor.com/device=gpu1" }; config2.env = std::map{ { "PATH", "/usr/local/bin" }, { "USER", "testuser" } }; @@ -115,6 +118,8 @@ TEST(RuntimeConfigTest, MergeConfigs) std::vector configs = { config1, config2 }; auto merged = linglong::utils::MergeRuntimeConfig(configs); + ASSERT_TRUE(merged.enablePipewireSocketMount.has_value()); + EXPECT_TRUE(*merged.enablePipewireSocketMount); ASSERT_TRUE(merged.disableXdp.has_value()); EXPECT_TRUE(*merged.disableXdp); @@ -154,6 +159,7 @@ TEST(RuntimeConfigTest, MergeEmptyConfigs) std::vector empty_configs; auto merged = linglong::utils::MergeRuntimeConfig(empty_configs); + EXPECT_FALSE(merged.enablePipewireSocketMount); EXPECT_FALSE(merged.disableXdp); EXPECT_FALSE(merged.deviceMode); EXPECT_FALSE(merged.env); @@ -165,6 +171,7 @@ TEST(RuntimeConfigTest, MergePartialConfigs) // Config with only env RuntimeConfigure config1; config1.disableXdp = false; + config1.enablePipewireSocketMount = false; config1.deviceMode = std::vector{ DeviceOption::Passthru }; config1.env = std::map{ { "PATH", "/usr/bin" } }; @@ -185,6 +192,8 @@ TEST(RuntimeConfigTest, MergePartialConfigs) nlohmann::json j; linglong::api::types::v1::to_json(j, merged); LogD("{}", j.dump()); + ASSERT_TRUE(merged.enablePipewireSocketMount.has_value()); + EXPECT_FALSE(*merged.enablePipewireSocketMount); ASSERT_TRUE(merged.disableXdp.has_value()); EXPECT_FALSE(*merged.disableXdp); diff --git a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.cpp b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.cpp index 6bfc426d2..13d4fd62d 100644 --- a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.cpp +++ b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.cpp @@ -356,6 +356,12 @@ utils::error::Result ContainerCfgBuilder::buildXDGRuntime() noexcept .source = xdpOption->docMountPoint / "by-app" / appId, .type = "bind" }); } + if (pipewireMountOption) { + bindIfExist(*runMount, + pipewireMountOption->hostSocketPath, + *containerXDGRuntimeDir / "pipewire-0", + false); + } return LINGLONG_OK; } @@ -1563,6 +1569,13 @@ ContainerCfgBuilder &ContainerCfgBuilder::enableXDP(XdpOption option) noexcept return *this; } +ContainerCfgBuilder & +ContainerCfgBuilder::enablePipewireSocketMount(PipewireMountOption option) noexcept +{ + pipewireMountOption = std::move(option); + return *this; +} + utils::error::Result ContainerCfgBuilder::buildContainerInfo() noexcept { LINGLONG_TRACE("build container info"); diff --git a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.h b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.h index 7605fe6ee..21a12ab98 100644 --- a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.h +++ b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/container_cfg_builder.h @@ -23,6 +23,11 @@ struct XdpOption std::filesystem::path docMountPoint; }; +struct PipewireMountOption +{ + std::filesystem::path hostSocketPath; +}; + enum class ANNOTATION { APPID, BASEDIR, @@ -139,6 +144,7 @@ class ContainerCfgBuilder ContainerCfgBuilder &enableQuirkVolatile() noexcept; ContainerCfgBuilder &enableXDP(XdpOption option) noexcept; + ContainerCfgBuilder &enablePipewireSocketMount(PipewireMountOption option) noexcept; ContainerCfgBuilder & setExtensionMounts(std::vector) noexcept; @@ -330,6 +336,7 @@ class ContainerCfgBuilder std::vector mountpoints; // this 'mounts' is used internally, distinct from config.mounts std::vector mounts; + std::optional pipewireMountOption; std::optional> overlayMerged; diff --git a/libs/utils/src/linglong/utils/runtime_config.cpp b/libs/utils/src/linglong/utils/runtime_config.cpp index 135ce52b0..cff6eecab 100644 --- a/libs/utils/src/linglong/utils/runtime_config.cpp +++ b/libs/utils/src/linglong/utils/runtime_config.cpp @@ -111,6 +111,10 @@ RuntimeConfigure MergeRuntimeConfig(const std::vector &configs result.disableXdp = config.disableXdp; } + if (config.enablePipewireSocketMount.has_value()) { + result.enablePipewireSocketMount = config.enablePipewireSocketMount; + } + if (config.deviceMode) { if (!result.deviceMode) { result.deviceMode = config.deviceMode;