Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2701,6 +2701,14 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_CommandArgDescription" xml:space="preserve">
<value>The command to run</value>
</data>
<data name="WSLCCLI_CpusArgDescription" xml:space="preserve">
<value>Number of CPUs (e.g. 0.5, 1, 2.5)</value>
<comment>{Locked="0.5"}{Locked="1"}{Locked="2.5"}Command line argument example values should not be translated</comment>
</data>
<data name="WSLCCLI_InvalidCpusError" xml:space="preserve">
<value>Invalid {} argument value: '{}'. Expected a positive number of CPUs (e.g. 0.5, 1, 2)</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated{Locked="0.5"}{Locked="1"}{Locked="2"}</comment>
</data>
<data name="WSLCCLI_ForceArgDescription" xml:space="preserve">
<value>Delete containers even if they are running</value>
</data>
Expand Down Expand Up @@ -2770,6 +2778,10 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_LatestArgDescription" xml:space="preserve">
<value>Show the latest created container (includes all states)</value>
</data>
<data name="WSLCCLI_MemoryArgDescription" xml:space="preserve">
<value>Memory limit (e.g. 512M, 1G)</value>
<comment>{Locked="512M"}{Locked="1G"}Command line argument example values should not be translated</comment>
</data>
<data name="WSLCCLI_HostnameArgDescription" xml:space="preserve">
<value>Container host name</value>
</data>
Expand Down Expand Up @@ -2903,6 +2915,14 @@ On first run, creates the file with all settings commented out at their defaults
<value>Mount tmpfs to the container at the given path</value>
<comment>{Locked="tmpfs"}Command line arguments should not be translated</comment>
</data>
<data name="WSLCCLI_UlimitArgDescription" xml:space="preserve">
<value>Ulimit options (format: &lt;name&gt;=&lt;soft&gt;[:&lt;hard&gt;], use -1 for unlimited)</value>
<comment>{Locked="-1"}{Locked="&lt;name&gt;=&lt;soft&gt;[:&lt;hard&gt;]"}Command line arguments should not be translated</comment>
</data>
<data name="WSLCCLI_InvalidUlimitError" xml:space="preserve">
<value>Invalid {} argument value: '{}'. Expected &lt;name&gt;=&lt;soft&gt;[:&lt;hard&gt;] (use -1 for unlimited)</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated{Locked="-1"}{Locked="&lt;name&gt;=&lt;soft&gt;[:&lt;hard&gt;]"}</comment>
</data>
<data name="WSLCCLI_UserArgDescription" xml:space="preserve">
<value>User ID for the process (name|uid|uid:gid)</value>
<comment>{Locked="name|uid|uid:gid"}Command line arguments should not be translated</comment>
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslc/arguments/ArgumentDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ _(BuildTarget, "target", NO_ALIAS, Kind::Value, L
_(CIDFile, "cidfile", NO_ALIAS, Kind::Value, Localization::WSLCCLI_CIDFileArgDescription()) \
_(Command, "command", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_CommandArgDescription()) \
_(ContainerId, "container-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ContainerIdArgDescription()) \
_(Cpus, "cpus", NO_ALIAS, Kind::Value, Localization::WSLCCLI_CpusArgDescription()) \
_(Force, "force", L"f", Kind::Flag, Localization::WSLCCLI_ForceArgDescription()) \
_(Detach, "detach", L"d", Kind::Flag, Localization::WSLCCLI_DetachArgDescription()) \
_(DNS, "dns", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSArgDescription()) \
Expand Down Expand Up @@ -72,6 +73,7 @@ _(Interactive, "interactive", L"i", Kind::Flag, L
_(Label, "label", L"l", Kind::Value, Localization::WSLCCLI_LabelArgDescription()) \
_(Last, "last", L"n", Kind::Value, Localization::WSLCCLI_LastArgDescription()) \
_(Latest, "latest", L"l", Kind::Flag, Localization::WSLCCLI_LatestArgDescription()) \
_(Memory, "memory", L"m", Kind::Value, Localization::WSLCCLI_MemoryArgDescription()) \
_(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
_(NetworkName, "network-name", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_NetworkNameArgDescription()) \
/*_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NoDNSArgDescription())*/ \
Expand Down Expand Up @@ -106,6 +108,7 @@ _(Time, "time", L"t", Kind::Value, L
_(TMPFS, "tmpfs", NO_ALIAS, Kind::Value, Localization::WSLCCLI_TMPFSArgDescription()) \
_(TTY, "tty", L"t", Kind::Flag, Localization::WSLCCLI_TTYArgDescription()) \
_(Type, "type", L"t", Kind::Value, Localization::WSLCCLI_TypeArgDescription()) \
_(Ulimit, "ulimit", NO_ALIAS, Kind::Value, Localization::WSLCCLI_UlimitArgDescription()) \
_(User, "user", L"u", Kind::Value, Localization::WSLCCLI_UserArgDescription()) \
_(Username, "username", L"u", Kind::Value, Localization::WSLCCLI_LoginUsernameArgDescription()) \
_(Verbose, "verbose", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_VerboseArgDescription()) \
Expand Down
91 changes: 91 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Module Name:
Implementation of the Argument Validation.

--*/

#include "precomp.h"
#include "Argument.h"
#include "ArgumentTypes.h"
#include "ArgumentValidation.h"
Expand Down Expand Up @@ -48,6 +50,18 @@ void Argument::Validate(const ArgMap& execArgs) const
validation::ValidateMemorySize(execArgs.GetAll<ArgType::ShmSize>(), m_name);
break;

case ArgType::Memory:
validation::ValidateMemorySize(execArgs.GetAll<ArgType::Memory>(), m_name);
break;

case ArgType::Cpus:
validation::ValidateNanoCpus(execArgs.GetAll<ArgType::Cpus>(), m_name);
break;

case ArgType::Ulimit:
validation::ValidateUlimit(execArgs.GetAll<ArgType::Ulimit>(), m_name);
break;

case ArgType::Tail:
validation::ValidateIntegerFromString<ULONGLONG>(
execArgs.GetAll<ArgType::Tail>(), m_name, [](auto value) { return value != 0; });
Expand Down Expand Up @@ -271,6 +285,83 @@ int64_t GetMemorySizeFromString(const std::wstring& input, const std::wstring& a
return static_cast<int64_t>(parsed.value());
}

void ValidateNanoCpus(const std::vector<std::wstring>& values, const std::wstring& argName)
{
for (const auto& value : values)
{
std::ignore = GetNanoCpusFromString(value, argName);
}
}

int64_t GetNanoCpusFromString(const std::wstring& input, const std::wstring& argName)
{
constexpr double NanosPerCpu = 1'000'000'000.0;
constexpr double MaxCpus = static_cast<double>(std::numeric_limits<int64_t>::max()) / NanosPerCpu;

const std::string narrow = WideToMultiByte(input);
const char* begin = narrow.c_str();
const char* end = begin + narrow.size();

double cpus{};
const auto result = std::from_chars(begin, end, cpus, std::chars_format::fixed);
if (result.ec != std::errc() || result.ptr != end || cpus <= 0.0 || cpus > MaxCpus)
{
throw ArgumentException(Localization::WSLCCLI_InvalidCpusError(argName, input));
}

return static_cast<int64_t>(cpus * NanosPerCpu);
}

void ValidateUlimit(const std::vector<std::wstring>& values, const std::wstring& argName)
{
for (const auto& value : values)
{
std::ignore = ParseUlimit(value, argName);
}
}

std::tuple<std::string, int64_t, int64_t> ParseUlimit(const std::wstring& input, const std::wstring& argName)
{
// Accepts <name>=<soft>[:<hard>]; if hard is omitted hard = soft. -1 means unlimited.
const auto equalsPos = input.find(L'=');
if (equalsPos == std::wstring::npos || equalsPos == 0)
{
throw ArgumentException(Localization::WSLCCLI_InvalidUlimitError(argName, input));
}

const std::wstring valuesPart = input.substr(equalsPos + 1);
const auto colonPos = valuesPart.find(L':');

auto parseLimit = [&](const std::wstring& limitStr) -> int64_t {
if (limitStr.empty())
{
throw ArgumentException(Localization::WSLCCLI_InvalidUlimitError(argName, input));
}

try
{
return GetIntegerFromString<int64_t>(limitStr, argName, [](int64_t v) { return v >= -1; });
}
catch (const ArgumentException&)
{
// Re-throw with the ulimit-specific error message so the user sees the full input.
throw ArgumentException(Localization::WSLCCLI_InvalidUlimitError(argName, input));
}
};

const int64_t soft = parseLimit(colonPos == std::wstring::npos ? valuesPart : valuesPart.substr(0, colonPos));
const int64_t hard = colonPos == std::wstring::npos ? soft : parseLimit(valuesPart.substr(colonPos + 1));

// This rejects "-1:1024" and "-1:<finite>" while allowing "<finite>:-1", "-1:-1", and "-1".
const bool invalidRange = (soft == -1) ? (hard != -1) : (hard != -1 && hard < soft);
if (invalidRange)
{
throw ArgumentException(Localization::WSLCCLI_InvalidUlimitError(argName, input));
}

return {WideToMultiByte(input.substr(0, equalsPos)), soft, hard};
}

std::pair<std::string, std::string> ParseLabel(const std::wstring& value)
{
std::pair<std::string, std::string> result{};
Expand Down
7 changes: 7 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Module Name:
#include "ContainerModel.h"
#include "InspectModel.h"
#include <string>
#include <tuple>
#include <vector>
#include <charconv>
#include <wslc.h>
Expand Down Expand Up @@ -65,6 +66,12 @@ WSLCSignal GetWSLCSignalFromString(const std::wstring& input, const std::wstring
void ValidateMemorySize(const std::vector<std::wstring>& values, const std::wstring& argName);
int64_t GetMemorySizeFromString(const std::wstring& input, const std::wstring& argName = {});

void ValidateNanoCpus(const std::vector<std::wstring>& values, const std::wstring& argName);
int64_t GetNanoCpusFromString(const std::wstring& input, const std::wstring& argName = {});

void ValidateUlimit(const std::vector<std::wstring>& values, const std::wstring& argName);
std::tuple<std::string, int64_t, int64_t> ParseUlimit(const std::wstring& input, const std::wstring& argName = {});

void ValidateFormatTypeFromString(const std::vector<std::wstring>& values, const std::wstring& argName);
FormatType GetFormatTypeFromString(const std::wstring& input, const std::wstring& argName = {});

Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslc/commands/ContainerCreateCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Command),
Argument::Create(ArgType::ForwardArgs),
Argument::Create(ArgType::CIDFile),
Argument::Create(ArgType::Cpus),
Argument::Create(ArgType::DNS, false, NO_LIMIT),
// Argument::Create(ArgType::DNSDomain),
Argument::Create(ArgType::DNSOption, false, NO_LIMIT),
Expand All @@ -45,6 +46,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::Hostname),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Label, false, NO_LIMIT),
Argument::Create(ArgType::Memory),
Argument::Create(ArgType::Name),
// Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Expand All @@ -57,6 +59,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
Argument::Create(ArgType::StopSignal),
Argument::Create(ArgType::TMPFS, false, NO_LIMIT),
Argument::Create(ArgType::TTY),
Argument::Create(ArgType::Ulimit, false, NO_LIMIT),
Argument::Create(ArgType::User),
Argument::Create(ArgType::Volume, false, NO_LIMIT),
// Argument::Create(ArgType::Virtual),
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslc/commands/ContainerRunCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::Command),
Argument::Create(ArgType::ForwardArgs),
Argument::Create(ArgType::CIDFile),
Argument::Create(ArgType::Cpus),
Argument::Create(ArgType::Detach),
Argument::Create(ArgType::DNS, false, NO_LIMIT),
// Argument::Create(ArgType::DNSDomain),
Expand All @@ -45,6 +46,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::Hostname),
Argument::Create(ArgType::Interactive),
Argument::Create(ArgType::Label, false, NO_LIMIT),
Argument::Create(ArgType::Memory),
Argument::Create(ArgType::Name),
// Argument::Create(ArgType::NoDNS),
// Argument::Create(ArgType::Progress),
Expand All @@ -58,6 +60,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
Argument::Create(ArgType::StopSignal),
Argument::Create(ArgType::TMPFS, false, NO_LIMIT),
Argument::Create(ArgType::TTY),
Argument::Create(ArgType::Ulimit, false, NO_LIMIT),
Argument::Create(ArgType::User),
Argument::Create(ArgType::Volume, false, NO_LIMIT),
// Argument::Create(ArgType::Virtual),
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslc/services/ContainerModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ struct ContainerOptions
std::vector<std::string> Tmpfs;
std::vector<std::pair<std::string, std::string>> Labels;
std::optional<std::wstring> CidFile{};
std::optional<int64_t> MemoryBytes{};
std::optional<int64_t> NanoCpus{};
std::vector<std::tuple<std::string, int64_t, int64_t>> Ulimits;
};

struct CreateContainerResult
Expand Down
15 changes: 15 additions & 0 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(
containerLauncher.SetShmSize(options.ShmSize.value());
}

if (options.MemoryBytes.has_value())
{
containerLauncher.SetMemoryLimit(options.MemoryBytes.value());
}

if (options.NanoCpus.has_value())
{
containerLauncher.SetNanoCpus(options.NanoCpus.value());
}

for (const auto& [name, soft, hard] : options.Ulimits)
{
containerLauncher.AddUlimit(name, soft, hard);
}

if (!options.Entrypoint.empty())
{
auto entrypoints = options.Entrypoint;
Expand Down
18 changes: 18 additions & 0 deletions src/windows/wslc/tasks/ContainerTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,24 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
options.ShmSize = validation::GetMemorySizeFromString(context.Args.Get<ArgType::ShmSize>());
}

if (context.Args.Contains(ArgType::Memory))
{
options.MemoryBytes = validation::GetMemorySizeFromString(context.Args.Get<ArgType::Memory>());
}

if (context.Args.Contains(ArgType::Cpus))
{
options.NanoCpus = validation::GetNanoCpusFromString(context.Args.Get<ArgType::Cpus>());
}

if (context.Args.Contains(ArgType::Ulimit))
{
for (const auto& value : context.Args.GetAll<ArgType::Ulimit>())
{
options.Ulimits.emplace_back(validation::ParseUlimit(value));
}
}

if (context.Args.Contains(ArgType::Command))
{
options.Arguments.emplace_back(WideToMultiByte(context.Args.Get<ArgType::Command>()));
Expand Down
Loading