diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index de258d29d..1c7be4b78 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2565,6 +2565,14 @@ On first run, creates the file with all settings commented out at their defaults
Remove the container after it stops
+
+ Signal to stop the container (default: {})
+ {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
+
+ Size of /dev/shm (e.g. 64m, 1g)
+ {Locked="/dev/shm"}
+
Session ID
diff --git a/src/windows/common/WSLCContainerLauncher.cpp b/src/windows/common/WSLCContainerLauncher.cpp
index 3d75c7d73..fc007421d 100644
--- a/src/windows/common/WSLCContainerLauncher.cpp
+++ b/src/windows/common/WSLCContainerLauncher.cpp
@@ -129,6 +129,11 @@ void WSLCContainerLauncher::SetDefaultStopSignal(WSLCSignal Signal)
m_stopSignal = Signal;
}
+void WSLCContainerLauncher::SetShmSize(ULONGLONG ShmSize)
+{
+ m_shmSize = ShmSize;
+}
+
void WSLCContainerLauncher::SetEntrypoint(std::vector&& entrypoint)
{
m_entrypoint = std::move(entrypoint);
@@ -254,6 +259,7 @@ std::pair> WSLCContainerLauncher::C
options.PortsCount = static_cast(m_ports.size());
options.StopSignal = m_stopSignal;
options.Flags = m_containerFlags;
+ options.ShmSize = m_shmSize;
if (!entrypointStorage.empty())
{
diff --git a/src/windows/common/WSLCContainerLauncher.h b/src/windows/common/WSLCContainerLauncher.h
index 4643bc875..e33b72c45 100644
--- a/src/windows/common/WSLCContainerLauncher.h
+++ b/src/windows/common/WSLCContainerLauncher.h
@@ -72,6 +72,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
void SetName(std::string&& Name);
void SetEntrypoint(std::vector&& entrypoint);
void SetDefaultStopSignal(WSLCSignal Signal);
+ void SetShmSize(ULONGLONG ShmSize);
void SetContainerFlags(WSLCContainerFlags Flags);
void SetHostname(std::string&& Hostname);
void SetDomainname(std::string&& Domainame);
@@ -95,6 +96,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
WSLCContainerNetworkType m_containerNetworkType;
std::vector m_entrypoint;
WSLCSignal m_stopSignal = WSLCSignalNone;
+ ULONGLONG m_shmSize = 0;
WSLCContainerFlags m_containerFlags = WSLCContainerFlagsNone;
std::string m_hostname;
std::string m_domainname;
diff --git a/src/windows/wslc/arguments/ArgumentDefinitions.h b/src/windows/wslc/arguments/ArgumentDefinitions.h
index a7bfe4e9b..569eb6a22 100644
--- a/src/windows/wslc/arguments/ArgumentDefinitions.h
+++ b/src/windows/wslc/arguments/ArgumentDefinitions.h
@@ -57,11 +57,11 @@ _(Follow, "follow", L"f", Kind::Flag, L
_(Format, "format", NO_ALIAS, Kind::Value, Localization::WSLCCLI_FormatArgDescription()) \
_(ForwardArgs, "arguments", NO_ALIAS, Kind::Forward, Localization::WSLCCLI_ForwardArgsDescription()) \
/*_(GroupId, "groupid", NO_ALIAS, Kind::Value, Localization::WSLCCLI_GroupIdArgDescription())*/ \
-_(Help, "help", WSLC_CLI_HELP_ARG, Kind::Flag, Localization::WSLCCLI_HelpArgDescription()) \
+_(Help, "help", WSLC_CLI_HELP_ARG, Kind::Flag, Localization::WSLCCLI_HelpArgDescription())\
_(Hostname, "hostname", L"h", Kind::Value, Localization::WSLCCLI_HostnameArgDescription()) \
_(ImageForce, "force", L"f", Kind::Flag, Localization::WSLCCLI_ImageForceArgDescription()) \
_(ImageId, "image", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ImageIdArgDescription()) \
-_(Input, "input", L"i", Kind::Value, Localization::WSLCCLI_InputArgDescription()) \
+_(Input, "input", L"i", Kind::Value, Localization::WSLCCLI_InputArgDescription())\
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
_(Label, "label", NO_ALIAS, Kind::Value, L"Volume metadata setting") \
_(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
@@ -85,9 +85,11 @@ _(Remove, "rm", NO_ALIAS, Kind::Flag, L
_(Server, "server", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_LoginServerArgDescription()) \
_(Session, "session", NO_ALIAS, Kind::Value, Localization::WSLCCLI_SessionIdArgDescription()) \
_(SessionId, "session-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_SessionIdPositionalArgDescription()) \
-_(StoragePath, "storage-path", NO_ALIAS, Kind::Positional, L"Path to the session storage directory") \
+_(ShmSize, "shm-size", NO_ALIAS, Kind::Value, Localization::WSLCCLI_ShmSizeArgDescription()) \
_(Signal, "signal", L"s", Kind::Value, Localization::WSLCCLI_SignalArgDescription(L"SIGKILL")) \
_(Source, "source", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_SourceArgDescription()) \
+_(StoragePath, "storage-path", NO_ALIAS, Kind::Positional, L"Path to the session storage directory") \
+_(StopSignal, "stop-signal", NO_ALIAS, Kind::Value, Localization::WSLCCLI_StopSignalArgDescription(L"SIGTERM")) \
_(Tag, "tag", L"t", Kind::Value, Localization::WSLCCLI_TagArgDescription()) \
_(Target, "target", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_TargetArgDescription()) \
_(Time, "time", L"t", Kind::Value, Localization::WSLCCLI_TimeArgDescription()) \
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index b96dadc4e..7666d4146 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -40,6 +40,14 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateWSLCSignalFromString(execArgs.GetAll(), m_name);
break;
+ case ArgType::StopSignal:
+ validation::ValidateWSLCSignalFromString(execArgs.GetAll(), m_name);
+ break;
+
+ case ArgType::ShmSize:
+ validation::ValidateMemorySize(execArgs.GetAll(), m_name);
+ break;
+
case ArgType::Time:
validation::ValidateIntegerFromString(execArgs.GetAll(), m_name);
break;
@@ -191,4 +199,24 @@ InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstri
}
}
+void ValidateMemorySize(const std::vector& values, const std::wstring& argName)
+{
+ for (const auto& value : values)
+ {
+ std::ignore = GetMemorySizeFromString(value, argName);
+ }
+}
+
+ULONGLONG GetMemorySizeFromString(const std::wstring& input, const std::wstring& argName)
+{
+ auto narrowInput = wsl::windows::common::string::WideToMultiByte(input);
+ auto parsed = wsl::shared::string::ParseMemorySize(narrowInput.c_str());
+ if (!parsed.has_value())
+ {
+ throw ArgumentException(std::format(L"Invalid {} argument value: '{}'. Expected a memory size (e.g. 64m, 256m, 1g)", argName, input));
+ }
+
+ return parsed.value();
+}
+
} // namespace wsl::windows::wslc::validation
diff --git a/src/windows/wslc/arguments/ArgumentValidation.h b/src/windows/wslc/arguments/ArgumentValidation.h
index 879582558..815336382 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.h
+++ b/src/windows/wslc/arguments/ArgumentValidation.h
@@ -58,6 +58,9 @@ T GetIntegerFromString(const std::wstring& value, const std::wstring& argName =
void ValidateWSLCSignalFromString(const std::vector& values, const std::wstring& argName);
WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring& argName = {});
+void ValidateMemorySize(const std::vector& values, const std::wstring& argName);
+ULONGLONG GetMemorySizeFromString(const std::wstring& input, const std::wstring& argName = {});
+
void ValidateFormatTypeFromString(const std::vector& values, const std::wstring& argName);
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});
diff --git a/src/windows/wslc/commands/ContainerCreateCommand.cpp b/src/windows/wslc/commands/ContainerCreateCommand.cpp
index 2135c0197..f6e48130f 100644
--- a/src/windows/wslc/commands/ContainerCreateCommand.cpp
+++ b/src/windows/wslc/commands/ContainerCreateCommand.cpp
@@ -51,6 +51,8 @@ std::vector ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Remove),
// Argument::Create(ArgType::Scheme),
Argument::Create(ArgType::Session),
+ Argument::Create(ArgType::ShmSize),
+ Argument::Create(ArgType::StopSignal),
Argument::Create(ArgType::TMPFS, false, NO_LIMIT),
Argument::Create(ArgType::TTY),
Argument::Create(ArgType::User),
diff --git a/src/windows/wslc/commands/ContainerRunCommand.cpp b/src/windows/wslc/commands/ContainerRunCommand.cpp
index 7899d3f79..35cda01f4 100644
--- a/src/windows/wslc/commands/ContainerRunCommand.cpp
+++ b/src/windows/wslc/commands/ContainerRunCommand.cpp
@@ -52,6 +52,8 @@ std::vector ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::Remove),
// Argument::Create(ArgType::Scheme),
Argument::Create(ArgType::Session),
+ Argument::Create(ArgType::ShmSize),
+ Argument::Create(ArgType::StopSignal),
Argument::Create(ArgType::TMPFS, false, NO_LIMIT),
Argument::Create(ArgType::TTY),
Argument::Create(ArgType::User),
diff --git a/src/windows/wslc/services/ContainerModel.h b/src/windows/wslc/services/ContainerModel.h
index 5997a2736..edad5c76f 100644
--- a/src/windows/wslc/services/ContainerModel.h
+++ b/src/windows/wslc/services/ContainerModel.h
@@ -37,6 +37,8 @@ struct ContainerOptions
bool Remove = false;
bool TTY = false;
bool PublishAll = false;
+ WSLCSignal StopSignal = WSLCSignalNone;
+ ULONGLONG ShmSize = 0;
std::vector Ports;
std::vector Volumes;
std::string WorkingDirectory;
diff --git a/src/windows/wslc/services/ContainerService.cpp b/src/windows/wslc/services/ContainerService.cpp
index 90af89e87..8e1383893 100644
--- a/src/windows/wslc/services/ContainerService.cpp
+++ b/src/windows/wslc/services/ContainerService.cpp
@@ -95,6 +95,16 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& sessio
containerLauncher.SetContainerFlags(containerFlags);
+ if (options.StopSignal != WSLCSignalNone)
+ {
+ containerLauncher.SetDefaultStopSignal(options.StopSignal);
+ }
+
+ if (options.ShmSize > 0)
+ {
+ containerLauncher.SetShmSize(options.ShmSize);
+ }
+
if (!options.Entrypoint.empty())
{
auto entrypoints = options.Entrypoint;
diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp
index 804f54282..52b713981 100644
--- a/src/windows/wslc/tasks/ContainerTasks.cpp
+++ b/src/windows/wslc/tasks/ContainerTasks.cpp
@@ -247,6 +247,16 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
options.Remove = true;
}
+ if (context.Args.Contains(ArgType::StopSignal))
+ {
+ options.StopSignal = validation::GetWSLCSignalFromString(context.Args.Get());
+ }
+
+ if (context.Args.Contains(ArgType::ShmSize))
+ {
+ options.ShmSize = validation::GetMemorySizeFromString(context.Args.Get());
+ }
+
if (context.Args.Contains(ArgType::Command))
{
options.Arguments.emplace_back(WideToMultiByte(context.Args.Get()));
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
index aa80ddd8e..456bf6527 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
@@ -651,6 +651,26 @@ class WSLCE2EContainerCreateTests
VERIFY_IS_TRUE(result.Stdout->find(L"options ndots:5 timeout:3") != std::wstring::npos);
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_StopSignal)
+ {
+ auto result = RunWslc(std::format(
+ L"container create --stop-signal SIGUSR1 --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ std::wstring containerId = result.GetStdoutOneLine();
+ VerifyContainerIsListed(containerId, L"created");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_ShmSize)
+ {
+ auto result = RunWslc(
+ std::format(L"container create --shm-size 128m --name {} {} df -h /dev/shm", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ result = RunWslc(std::format(L"container start -a {}", WslcContainerName));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"128M") != std::wstring::npos);
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
@@ -724,6 +744,8 @@ class WSLCE2EContainerCreateTests
<< L" -P,--publish-all Publish all exposed ports to random host ports\r\n"
<< L" --rm Remove the container after it stops\r\n"
<< L" --session Specify the session to use\r\n"
+ << L" --shm-size Size of /dev/shm (e.g. 64m, 1g)\r\n"
+ << L" --stop-signal Signal to stop the container (default: SIGTERM)\r\n"
<< L" --tmpfs Mount tmpfs to the container at the given path\r\n"
<< L" -t,--tty Open a TTY with the container process.\r\n"
<< L" -u,--user User ID for the process (name|uid|uid:gid)\r\n"
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
index 506c2e81b..6886b9ea4 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
@@ -641,6 +641,22 @@ class WSLCE2EContainerRunTests
result.Verify({.Stderr = std::format(L"Volume not found: '{}'\r\nError code: WSLC_E_VOLUME_NOT_FOUND\r\n", WslcVolumeName), .ExitCode = 1});
}
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_StopSignal)
+ {
+ auto result = RunWslc(std::format(
+ L"container run -d --stop-signal SIGUSR1 --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto containerId = result.GetStdoutOneLine();
+ VerifyContainerIsListed(containerId, L"running");
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_ShmSize)
+ {
+ auto result = RunWslc(std::format(L"container run --rm --shm-size 128m {} df -h /dev/shm", DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_IS_TRUE(result.Stdout->find(L"128M") != std::wstring::npos);
+ }
+
private:
// Test container name
const std::wstring WslcContainerName = L"wslc-test-container";
@@ -720,6 +736,8 @@ class WSLCE2EContainerRunTests
<< L" -P,--publish-all Publish all exposed ports to random host ports\r\n"
<< L" --rm Remove the container after it stops\r\n"
<< L" --session Specify the session to use\r\n"
+ << L" --shm-size Size of /dev/shm (e.g. 64m, 1g)\r\n"
+ << L" --stop-signal Signal to stop the container (default: SIGTERM)\r\n"
<< L" --tmpfs Mount tmpfs to the container at the given path\r\n"
<< L" -t,--tty Open a TTY with the container process.\r\n"
<< L" -u,--user User ID for the process (name|uid|uid:gid)\r\n"