Skip to content

Commit 2dadc44

Browse files
committed
feat: add PipeWire socket mount support for sandbox
1. Added `enable-pipewire` flag to CLI for enabling PipeWire socket mount 2. Extended API schema v1 with `enable_pipewire` option in RuntimeConfigure 3. Implemented mount logic in ContainerCfgBuilder to bind host PipeWire socket into container 4. Enhanced container_builder to resolve XDG runtime directory and mount socket 5. Added unit tests covering runtime config and CLI option propagation 6. Updated merge logic for runtime configs to handle PipeWire setting 7. Integrated option flow from CLI, runtime config, and JSON serialization/deserialization Log: Added --enable-pipewire option to mount PipeWire audio socket inside sandbox for audio support Influence: 1. Test CLI flag with `--enable-pipewire` on container run, verify socket is mounted 2. Test runtime config JSON with `enable_pipewire: true`, verify mount behavior 3. Test mount failure when host socket does not exist or is not a socket 4. Test config merge: partial configs, overwrite order, and default behavior 5. Verify socket accessibility inside container (permissions, path) 6. Test combined with other options like --disable-xdp to confirm no conflicts 7. Test on systems with and without PipeWire installed 8. Verify backward compatibility for existing configs without the new field
1 parent 4ce98a9 commit 2dadc44

13 files changed

Lines changed: 120 additions & 0 deletions

File tree

api/schema/v1.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,9 @@
14581458
"disable_xdp": {
14591459
"type": "boolean"
14601460
},
1461+
"enable_pipewire": {
1462+
"type": "boolean"
1463+
},
14611464
"mounts": {
14621465
"type": "array",
14631466
"items": {

api/schema/v1.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,8 @@ $defs:
11031103
$ref: '#/$defs/DeviceOption'
11041104
disable_xdp:
11051105
type: boolean
1106+
enable_pipewire:
1107+
type: boolean
11061108
mounts:
11071109
type: array
11081110
items:

apps/ll-cli/src/main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ ll-cli run org.deepin.demo -- bash -x /path/to/bash/script)"));
179179
runOptions.disableXdp,
180180
_("Enable or disable xdg-desktop-portal related integration inside the sandbox"))
181181
->take_last();
182+
cliRun
183+
->add_flag("--enable-pipewire",
184+
runOptions.enablePipewireSocketMount,
185+
_("Enable PipeWire socket mount inside the sandbox"))
186+
->take_last();
182187
cliRun->add_option("--run-context", runOptions.runContext, _("Run context json string"))
183188
->group("");
184189
cliRun

libs/api/src/linglong/api/types/v1/Generators.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,7 @@ inline void from_json(const json & j, RuntimeConfigure& x) {
13131313
x.deviceMode = get_stack_optional<std::vector<DeviceOption>>(j, "device_mode");
13141314
x.devices = get_stack_optional<std::vector<std::string>>(j, "devices");
13151315
x.disableXdp = get_stack_optional<bool>(j, "disable_xdp");
1316+
x.enablePipewireSocketMount = get_stack_optional<bool>(j, "enable_pipewire");
13161317
x.env = get_stack_optional<std::map<std::string, std::string>>(j, "env");
13171318
x.extDefs = get_stack_optional<std::map<std::string, std::vector<ExtensionDefine>>>(j, "ext_defs");
13181319
x.instances = get_stack_optional<std::map<std::string, RuntimeConfigure>>(j, "instances");
@@ -1330,6 +1331,9 @@ j["devices"] = x.devices;
13301331
if (x.disableXdp) {
13311332
j["disable_xdp"] = x.disableXdp;
13321333
}
1334+
if (x.enablePipewireSocketMount) {
1335+
j["enable_pipewire"] = x.enablePipewireSocketMount;
1336+
}
13331337
if (x.env) {
13341338
j["env"] = x.env;
13351339
}

libs/api/src/linglong/api/types/v1/RuntimeConfigure.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct RuntimeConfigure {
4040
std::optional<std::vector<DeviceOption>> deviceMode;
4141
std::optional<std::vector<std::string>> devices;
4242
std::optional<bool> disableXdp;
43+
std::optional<bool> enablePipewireSocketMount;
4344
std::optional<std::map<std::string, std::string>> env;
4445
/**
4546
* external extension definitions to extend the component

libs/linglong/src/linglong/cli/cli.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct RunOptions
5858
std::optional<std::string> workdir;
5959
std::vector<std::string> extensions;
6060
std::optional<bool> disableXdp;
61+
std::optional<bool> enablePipewireSocketMount;
6162
bool privileged{ false };
6263
std::vector<std::string> capsAdd;
6364
std::vector<std::string> cdiSpecDir = { "/etc/cdi", "/var/run/cdi" };

libs/linglong/src/linglong/runtime/container_builder.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "linglong/cli/cli.h"
1010
#include "linglong/common/dir.h"
1111
#include "linglong/common/strings.h"
12+
#include "linglong/common/xdg.h"
1213
#include "linglong/oci-cfg-generators/container_cfg_builder.h"
1314
#include "linglong/package/architecture.h"
1415
#include "linglong/runtime/run_context.h"
@@ -130,6 +131,10 @@ auto RunContainerOptions::applyRuntimeConfig(
130131
this->disableXdp = *runtimeConfig.disableXdp;
131132
}
132133

134+
if (runtimeConfig.enablePipewireSocketMount.has_value()) {
135+
this->enablePipewireSocketMount = *runtimeConfig.enablePipewireSocketMount;
136+
}
137+
133138
if (runtimeConfig.deviceMode) {
134139
for (const auto &option : *runtimeConfig.deviceMode) {
135140
if (option == api::types::v1::DeviceOption::Passthru) {
@@ -172,6 +177,10 @@ auto RunContainerOptions::applyCliRunOptions(const cli::RunOptions &options) noe
172177
if (options.disableXdp.has_value()) {
173178
this->disableXdp = *options.disableXdp;
174179
}
180+
181+
if (options.enablePipewireSocketMount.has_value()) {
182+
this->enablePipewireSocketMount = *options.enablePipewireSocketMount;
183+
}
175184
this->privileged = options.privileged;
176185
this->capabilities.insert(this->capabilities.end(),
177186
options.capsAdd.begin(),
@@ -211,6 +220,11 @@ auto RunContainerOptions::isDevicePassthruEnabled() const noexcept -> bool
211220
return this->devicePassthru;
212221
}
213222

223+
auto RunContainerOptions::isPipewireSocketMountEnabled() const noexcept -> bool
224+
{
225+
return this->enablePipewireSocketMount;
226+
}
227+
214228
auto RunContainerOptions::isXdpDisabled() const noexcept -> bool
215229
{
216230
return this->disableXdp;
@@ -590,6 +604,11 @@ auto ContainerBuilder::configureRunContainer(PreparedContainer &prepared,
590604
docMountPoint.error());
591605
}
592606
}
607+
if (options.isPipewireSocketMountEnabled()) {
608+
auto pwSocketPath = common::xdg::getXDGRuntimeDir() / "pipewire-0";
609+
prepared.cfgBuilder.enablePipewireSocketMount(
610+
generator::PipewireMountOption{ .hostSocketPath = std::move(pwSocketPath) });
611+
}
593612

594613
if (options.isDevicePassthruEnabled()) {
595614
prepared.cfgBuilder.bindDev(true);

libs/linglong/src/linglong/runtime/container_builder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ struct RunContainerOptions
5454
-> const std::vector<SecurityContextType> &;
5555
[[nodiscard]] auto isDevicePassthruEnabled() const noexcept -> bool;
5656
[[nodiscard]] auto isXdpDisabled() const noexcept -> bool;
57+
[[nodiscard]] auto isPipewireSocketMountEnabled() const noexcept -> bool;
5758
[[nodiscard]] auto isPrivileged() const noexcept -> bool;
5859

5960
CommonContainerOptions common;
6061

6162
bool disableXdp{ false };
63+
bool enablePipewireSocketMount{ false };
6264
bool privileged{ false };
6365
bool devicePassthru{ false };
6466
std::map<std::string, std::string> env;

libs/linglong/tests/ll-tests/src/linglong/runtime/container_builder_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,51 @@ TEST(RunContainerOptionsTest, ApplyCliRunOptionsOverridesRuntimeConfigXdpSetting
8080
ASSERT_TRUE(cliResult);
8181
EXPECT_TRUE(options.isXdpDisabled());
8282
}
83+
84+
TEST(RunContainerOptionsTest, ApplyRuntimeConfigEnablesPipewireSocketMount)
85+
{
86+
runtime::RunContainerOptions options;
87+
88+
api::types::v1::RuntimeConfigure runtimeConfig;
89+
runtimeConfig.enablePipewireSocketMount = true;
90+
91+
auto result = options.applyRuntimeConfig(runtimeConfig);
92+
ASSERT_TRUE(result);
93+
EXPECT_TRUE(options.isPipewireSocketMountEnabled());
94+
}
95+
96+
TEST(RunContainerOptionsTest, ApplyCliRunOptionsPreservesRuntimeConfigPipewireSettingByDefault)
97+
{
98+
runtime::RunContainerOptions options;
99+
100+
api::types::v1::RuntimeConfigure runtimeConfig;
101+
runtimeConfig.enablePipewireSocketMount = true;
102+
103+
auto runtimeResult = options.applyRuntimeConfig(runtimeConfig);
104+
ASSERT_TRUE(runtimeResult);
105+
ASSERT_TRUE(options.isPipewireSocketMountEnabled());
106+
107+
cli::RunOptions runOptions;
108+
auto cliResult = options.applyCliRunOptions(runOptions);
109+
ASSERT_TRUE(cliResult);
110+
EXPECT_TRUE(options.isPipewireSocketMountEnabled());
111+
}
112+
113+
TEST(RunContainerOptionsTest, ApplyCliRunOptionsOverridesRuntimeConfigPipewireSettingWhenSpecified)
114+
{
115+
runtime::RunContainerOptions options;
116+
117+
api::types::v1::RuntimeConfigure runtimeConfig;
118+
runtimeConfig.enablePipewireSocketMount = false;
119+
120+
auto runtimeResult = options.applyRuntimeConfig(runtimeConfig);
121+
ASSERT_TRUE(runtimeResult);
122+
ASSERT_FALSE(options.isPipewireSocketMountEnabled());
123+
124+
cli::RunOptions runOptions;
125+
runOptions.enablePipewireSocketMount = true;
126+
127+
auto cliResult = options.applyCliRunOptions(runOptions);
128+
ASSERT_TRUE(cliResult);
129+
EXPECT_TRUE(options.isPipewireSocketMountEnabled());
130+
}

libs/linglong/tests/ll-tests/src/linglong/utils/runtime_config_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ TEST(RuntimeConfigTest, LoadFromPath)
2828

2929
RuntimeConfigure config;
3030
config.disableXdp = true;
31+
config.enablePipewireSocketMount = true;
3132
config.deviceMode = std::vector<DeviceOption>{ DeviceOption::Passthru };
3233
config.env =
3334
std::map<std::string, std::string>{ { "PATH", "/usr/bin" }, { "HOME", "/home/user" } };
@@ -84,6 +85,7 @@ TEST(RuntimeConfigTest, MergeConfigs)
8485
RuntimeConfigure config1;
8586
config1.disableXdp = false;
8687
config1.deviceMode = std::vector<DeviceOption>{ DeviceOption::Passthru };
88+
config1.enablePipewireSocketMount = false;
8789
config1.devices = std::vector<std::string>{ "vendor.com/device=gpu0" };
8890
config1.env =
8991
std::map<std::string, std::string>{ { "PATH", "/usr/bin" }, { "HOME", "/home/user1" } };
@@ -98,6 +100,7 @@ TEST(RuntimeConfigTest, MergeConfigs)
98100
RuntimeConfigure config2;
99101
config2.disableXdp = true;
100102
config2.deviceMode = std::vector<DeviceOption>{ DeviceOption::Passthru };
103+
config2.enablePipewireSocketMount = true;
101104
config2.devices = std::vector<std::string>{ "vendor.com/device=gpu1" };
102105
config2.env =
103106
std::map<std::string, std::string>{ { "PATH", "/usr/local/bin" }, { "USER", "testuser" } };
@@ -115,6 +118,8 @@ TEST(RuntimeConfigTest, MergeConfigs)
115118
std::vector<RuntimeConfigure> configs = { config1, config2 };
116119
auto merged = linglong::utils::MergeRuntimeConfig(configs);
117120

121+
ASSERT_TRUE(merged.enablePipewireSocketMount.has_value());
122+
EXPECT_TRUE(*merged.enablePipewireSocketMount);
118123
ASSERT_TRUE(merged.disableXdp.has_value());
119124
EXPECT_TRUE(*merged.disableXdp);
120125

@@ -154,6 +159,7 @@ TEST(RuntimeConfigTest, MergeEmptyConfigs)
154159
std::vector<RuntimeConfigure> empty_configs;
155160
auto merged = linglong::utils::MergeRuntimeConfig(empty_configs);
156161

162+
EXPECT_FALSE(merged.enablePipewireSocketMount);
157163
EXPECT_FALSE(merged.disableXdp);
158164
EXPECT_FALSE(merged.deviceMode);
159165
EXPECT_FALSE(merged.env);
@@ -165,6 +171,7 @@ TEST(RuntimeConfigTest, MergePartialConfigs)
165171
// Config with only env
166172
RuntimeConfigure config1;
167173
config1.disableXdp = false;
174+
config1.enablePipewireSocketMount = false;
168175
config1.deviceMode = std::vector<DeviceOption>{ DeviceOption::Passthru };
169176
config1.env = std::map<std::string, std::string>{ { "PATH", "/usr/bin" } };
170177

@@ -185,6 +192,8 @@ TEST(RuntimeConfigTest, MergePartialConfigs)
185192
nlohmann::json j;
186193
linglong::api::types::v1::to_json(j, merged);
187194
LogD("{}", j.dump());
195+
ASSERT_TRUE(merged.enablePipewireSocketMount.has_value());
196+
EXPECT_FALSE(*merged.enablePipewireSocketMount);
188197

189198
ASSERT_TRUE(merged.disableXdp.has_value());
190199
EXPECT_FALSE(*merged.disableXdp);

0 commit comments

Comments
 (0)