Skip to content

Commit ca6a44e

Browse files
reddevillgdengbo11
authored andcommitted
feat: add device passthru mode
Add --device-mode option to allow containers to share the host's /dev directory. Signed-off-by: reddevillg <reddevillg@gmail.com>
1 parent b2aa748 commit ca6a44e

17 files changed

Lines changed: 240 additions & 29 deletions

File tree

api/schema/v1.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,12 @@
14481448
"$ref": "#/$defs/ExtensionDefine"
14491449
}
14501450
}
1451+
},
1452+
"device_mode": {
1453+
"type": "array",
1454+
"items": {
1455+
"$ref": "#/$defs/DeviceOption"
1456+
}
14511457
}
14521458
}
14531459
},
@@ -1530,6 +1536,12 @@
15301536
}
15311537
}
15321538
}
1539+
},
1540+
"DeviceOption": {
1541+
"description": "device option",
1542+
"enum": [
1543+
"passthru"
1544+
]
15331545
}
15341546
},
15351547
"type": "object",
@@ -1680,6 +1692,9 @@
16801692
},
16811693
"CDIDeviceEntry": {
16821694
"$ref": "#/$defs/CDIDeviceEntry"
1695+
},
1696+
"DeviceOption": {
1697+
"$ref": "#/$defs/DeviceOption"
16831698
}
16841699
}
16851700
}

api/schema/v1.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,10 @@ $defs:
10971097
type: array
10981098
items:
10991099
$ref: '#/$defs/ExtensionDefine'
1100+
device_mode:
1101+
type: array
1102+
items:
1103+
$ref: '#/$defs/DeviceOption'
11001104
RunContextConfig:
11011105
title: RunContextConfig
11021106
type: object
@@ -1153,6 +1157,9 @@ $defs:
11531157
checksum:
11541158
description: CDI device spec checksum
11551159
type: string
1160+
DeviceOption:
1161+
description: device option
1162+
enum: ["passthru"]
11561163
type: object
11571164
properties:
11581165
# NOTE: "properties" is auto generated by referring all types is $defs

apps/ll-cli/src/main.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <algorithm>
3333
#include <functional>
34+
#include <map>
3435
#include <memory>
3536
#include <string_view>
3637
#include <thread>
@@ -185,9 +186,16 @@ ll-cli run org.deepin.demo -- bash -x /path/to/bash/script)"));
185186
->delimiter(',')
186187
->capture_default_str()
187188
->allow_extra_args(false);
188-
cliRun->add_option("--device", runOptions.devices, _("Add CDI devices"))
189+
cliRun->add_option("--device", runOptions.cdiDevices, _("Add CDI devices"))
189190
->delimiter(',')
190191
->allow_extra_args(false);
192+
const std::map<std::string, linglong::api::types::v1::DeviceOption> deviceOptionMap = {
193+
{ "passthru", linglong::api::types::v1::DeviceOption::Passthru },
194+
};
195+
cliRun->add_option("--device-mode", runOptions.deviceOptions, _("Add device options"))
196+
->delimiter(',')
197+
->transform(CLI::CheckedTransformer(deviceOptionMap, CLI::ignore_case))
198+
->allow_extra_args(false);
191199
cliRun->add_option("COMMAND", runOptions.commands, _("Run commands in a running sandbox"));
192200
}
193201

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// This file is generated by tools/codegen.sh
2+
// DO NOT EDIT IT.
3+
4+
// clang-format off
5+
6+
// To parse this JSON data, first install
7+
//
8+
// json.hpp https://github.com/nlohmann/json
9+
//
10+
// Then include this file, and then do
11+
//
12+
// DeviceOption.hpp data = nlohmann::json::parse(jsonString);
13+
14+
#pragma once
15+
16+
#include <optional>
17+
#include <nlohmann/json.hpp>
18+
#include "linglong/api/types/v1/helper.hpp"
19+
20+
namespace linglong {
21+
namespace api {
22+
namespace types {
23+
namespace v1 {
24+
/**
25+
* device option
26+
*/
27+
28+
using nlohmann::json;
29+
30+
/**
31+
* device option
32+
*/
33+
enum class DeviceOption : int { Passthru };
34+
}
35+
}
36+
}
37+
}
38+
39+
// clang-format on

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "linglong/api/types/v1/ExportDirs.hpp"
6161
#include "linglong/api/types/v1/DialogMessage.hpp"
6262
#include "linglong/api/types/v1/DialogHandShakePayload.hpp"
63+
#include "linglong/api/types/v1/DeviceOption.hpp"
6364
#include "linglong/api/types/v1/ContainerProcessStateInfo.hpp"
6465
#include "linglong/api/types/v1/CommonResult.hpp"
6566
#include "linglong/api/types/v1/CommonOptions.hpp"
@@ -265,6 +266,9 @@ void to_json(json & j, const UpgradeListResult & x);
265266
void from_json(const json & j, LinglongAPIV1 & x);
266267
void to_json(json & j, const LinglongAPIV1 & x);
267268

269+
void from_json(const json & j, DeviceOption & x);
270+
void to_json(json & j, const DeviceOption & x);
271+
268272
void from_json(const json & j, InteractionMessageType & x);
269273
void to_json(json & j, const InteractionMessageType & x);
270274

@@ -1273,12 +1277,16 @@ j["version"] = x.version;
12731277
}
12741278

12751279
inline void from_json(const json & j, RuntimeConfigure& x) {
1280+
x.deviceMode = get_stack_optional<std::vector<DeviceOption>>(j, "device_mode");
12761281
x.env = get_stack_optional<std::map<std::string, std::string>>(j, "env");
12771282
x.extDefs = get_stack_optional<std::map<std::string, std::vector<ExtensionDefine>>>(j, "ext_defs");
12781283
}
12791284

12801285
inline void to_json(json & j, const RuntimeConfigure & x) {
12811286
j = json::object();
1287+
if (x.deviceMode) {
1288+
j["device_mode"] = x.deviceMode;
1289+
}
12821290
if (x.env) {
12831291
j["env"] = x.env;
12841292
}
@@ -1357,6 +1365,7 @@ x.commonOptions = get_stack_optional<CommonOptions>(j, "CommonOptions");
13571365
x.commonResult = get_stack_optional<CommonResult>(j, "CommonResult");
13581366
x.containerProcessStateInfo = get_stack_optional<ContainerProcessStateInfo>(j, "ContainerProcessStateInfo");
13591367
x.deviceNode = get_stack_optional<DeviceNode>(j, "DeviceNode");
1368+
x.deviceOption = get_stack_optional<DeviceOption>(j, "DeviceOption");
13601369
x.dialogHandShakePayload = get_stack_optional<DialogHandShakePayload>(j, "DialogHandShakePayload");
13611370
x.dialogMessage = get_stack_optional<DialogMessage>(j, "DialogMessage");
13621371
x.exportDirs = get_stack_optional<ExportDirs>(j, "ExportDirs");
@@ -1432,6 +1441,9 @@ j["ContainerProcessStateInfo"] = x.containerProcessStateInfo;
14321441
if (x.deviceNode) {
14331442
j["DeviceNode"] = x.deviceNode;
14341443
}
1444+
if (x.deviceOption) {
1445+
j["DeviceOption"] = x.deviceOption;
1446+
}
14351447
if (x.dialogHandShakePayload) {
14361448
j["DialogHandShakePayload"] = x.dialogHandShakePayload;
14371449
}
@@ -1548,6 +1560,18 @@ j["XDGDirectoryPermissions"] = x.xdgDirectoryPermissions;
15481560
}
15491561
}
15501562

1563+
inline void from_json(const json & j, DeviceOption & x) {
1564+
if (j == "passthru") x = DeviceOption::Passthru;
1565+
else { throw std::runtime_error("Input JSON does not conform to schema!"); }
1566+
}
1567+
1568+
inline void to_json(json & j, const DeviceOption & x) {
1569+
switch (x) {
1570+
case DeviceOption::Passthru: j = "passthru"; break;
1571+
default: throw std::runtime_error("Unexpected value in enumeration \"DeviceOption\": " + std::to_string(static_cast<int>(x)));
1572+
}
1573+
}
1574+
15511575
inline void from_json(const json & j, InteractionMessageType & x) {
15521576
if (j == "Downgrade") x = InteractionMessageType::Downgrade;
15531577
else if (j == "Install") x = InteractionMessageType::Install;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ namespace linglong {
6767
namespace api {
6868
namespace types {
6969
namespace v1 {
70+
enum class DeviceOption : int;
7071
enum class InteractionMessageType : int;
7172
enum class State : int;
7273
}
@@ -101,6 +102,7 @@ std::optional<CommonOptions> commonOptions;
101102
std::optional<CommonResult> commonResult;
102103
std::optional<ContainerProcessStateInfo> containerProcessStateInfo;
103104
std::optional<DeviceNode> deviceNode;
105+
std::optional<DeviceOption> deviceOption;
104106
std::optional<DialogHandShakePayload> dialogHandShakePayload;
105107
std::optional<DialogMessage> dialogMessage;
106108
std::optional<ExportDirs> exportDirs;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,24 @@
1919

2020
#include "linglong/api/types/v1/ExtensionDefine.hpp"
2121

22+
namespace linglong {
23+
namespace api {
24+
namespace types {
25+
namespace v1 {
26+
enum class DeviceOption : int;
27+
}
28+
}
29+
}
30+
}
31+
2232
namespace linglong {
2333
namespace api {
2434
namespace types {
2535
namespace v1 {
2636
using nlohmann::json;
2737

2838
struct RuntimeConfigure {
39+
std::optional<std::vector<DeviceOption>> deviceMode;
2940
std::optional<std::map<std::string, std::string>> env;
3041
/**
3142
* external extension definitions to extend the component

libs/linglong/src/linglong/cli/cli.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,8 +623,8 @@ int Cli::run(const RunOptions &options)
623623
if (runtimeConfig && runtimeConfig->extDefs) {
624624
opts.externalExtensionDefs = std::move(runtimeConfig->extDefs).value();
625625
}
626-
if (!options.devices.empty()) {
627-
auto cdiDevices = cdi::getCDIDevices(options.cdiSpecDir, options.devices);
626+
if (!options.cdiDevices.empty()) {
627+
auto cdiDevices = cdi::getCDIDevices(options.cdiSpecDir, options.cdiDevices);
628628
if (!cdiDevices) {
629629
handleCommonError(cdiDevices.error());
630630
return -1;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ struct RunOptions
6060
bool privileged{ false };
6161
std::vector<std::string> capsAdd;
6262
std::vector<std::string> cdiSpecDir = { "/etc/cdi", "/var/run/cdi" };
63-
std::vector<std::string> devices;
63+
std::vector<std::string> cdiDevices;
64+
std::vector<api::types::v1::DeviceOption> deviceOptions;
6465
};
6566

6667
struct EnterOptions

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ std::string genContainerID(const api::types::v1::RunContextConfig &config) noexc
6868
auto RunContainerOptions::applyRuntimeConfig(
6969
const api::types::v1::RuntimeConfigure &runtimeConfig) noexcept -> utils::error::Result<void>
7070
{
71+
if (runtimeConfig.deviceMode) {
72+
for (const auto &option : *runtimeConfig.deviceMode) {
73+
if (option == api::types::v1::DeviceOption::Passthru) {
74+
this->devicePassthru = true;
75+
break;
76+
}
77+
}
78+
}
79+
7180
if (runtimeConfig.env) {
7281
for (const auto &[key, value] : *runtimeConfig.env) {
7382
this->env.insert_or_assign(key, value);
@@ -82,6 +91,13 @@ auto RunContainerOptions::applyCliRunOptions(const cli::RunOptions &options) noe
8291
{
8392
LINGLONG_TRACE("apply cli run options to run config");
8493

94+
for (const auto &option : options.deviceOptions) {
95+
if (option == api::types::v1::DeviceOption::Passthru) {
96+
this->devicePassthru = true;
97+
break;
98+
}
99+
}
100+
85101
for (const auto &item : options.envs) {
86102
auto split = item.find('=');
87103
if (split == std::string::npos || split == 0) {
@@ -125,6 +141,11 @@ auto RunContainerOptions::getSecurityContexts() const noexcept
125141
return this->securityContexts;
126142
}
127143

144+
auto RunContainerOptions::isDevicePassthruEnabled() const noexcept -> bool
145+
{
146+
return this->devicePassthru;
147+
}
148+
128149
auto RunContainerOptions::isPrivileged() const noexcept -> bool
129150
{
130151
return this->privileged;
@@ -441,7 +462,6 @@ auto ContainerBuilder::configureRunContainer(PreparedContainer &prepared,
441462
std::filesystem::path homePath{ homeEnv };
442463

443464
prepared.cfgBuilder.setAnnotation(generator::ANNOTATION::LAST_PID, std::to_string(::getpid()))
444-
.bindDevNode()
445465
.bindXDGRuntime()
446466
.bindRemovableStorageMounts()
447467
.bindHostRoot()
@@ -453,6 +473,12 @@ auto ContainerBuilder::configureRunContainer(PreparedContainer &prepared,
453473
.bindIPC()
454474
.forwardDefaultEnv();
455475

476+
if (options.isDevicePassthruEnabled()) {
477+
prepared.cfgBuilder.bindDev(true);
478+
} else {
479+
prepared.cfgBuilder.bindDevNode();
480+
}
481+
456482
std::error_code ec;
457483
auto socketDir = prepared.context->getBundleDir() / "init";
458484
std::filesystem::create_directories(socketDir, ec);

0 commit comments

Comments
 (0)