Skip to content
Merged
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
9 changes: 8 additions & 1 deletion src/windows/wslc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
# Object library for WSLC components.
# Used to build the executable and also unit testing components.
add_library(wslclib OBJECT ${SOURCES} ${HEADERS})
set_target_properties(wslclib PROPERTIES FOLDER windows)
set_target_properties(wslclib PROPERTIES
# TODO: UNITY builds for wslclib are currently disabled because they have not been
# validated for build performance and diagnostics in this project. Re-enable
# the following properties once UNITY builds have been evaluated and approved:
# UNITY_BUILD ON
# UNITY_BUILD_BATCH_SIZE 0 # 0 means CMake decides automatically
FOLDER windows
)
target_include_directories(wslclib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/core
Expand Down
4 changes: 4 additions & 0 deletions src/windows/wslc/arguments/Argument.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Module Name:
#define WSLC_CLI_HELP_ARG L"h"
#define WSLC_CLI_HELP_ARG_STRING WSLC_CLI_ARG_ID_STRING WSLC_CLI_HELP_ARG
#define NO_ALIAS L""
#define NO_LIMIT -1

using namespace wsl::windows::wslc::argument;

Expand Down Expand Up @@ -93,6 +94,9 @@ struct Argument
return m_countLimit;
}

// Validates this argument's value in the provided args
void Validate(const ArgMap& execArgs) const;

private:
ArgType m_argType;
std::wstring m_name;
Expand Down
34 changes: 29 additions & 5 deletions src/windows/wslc/arguments/ArgumentDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,40 @@ Module Name:
// clang-format off
#define WSLC_ARGUMENTS(_) \
_(All, "all", L"a", Kind::Flag, L"Show all regardless of state.") \
_(Attach, "attach", L"a", Kind::Flag, Localization::WSLCCLI_AttachArgDescription()) \
_(CIDFile, "cidfile", NO_ALIAS, Kind::Value, L"Write the container ID to the provided path.") \
_(Command, "command", NO_ALIAS, Kind::Positional, L"The command to run") \
_(ContainerId, "container-id", NO_ALIAS, Kind::Positional, L"Specify the target container by its ID") \
_(ContainerId, "container-id", NO_ALIAS, Kind::Positional, Localization::WSLCCLI_ContainerIdArgDescription()) \
_(Detach, "detach", L"d", Kind::Flag, L"Run container in detached mode") \
_(DNS, "dns", NO_ALIAS, Kind::Value, L"IP address of the DNS nameserver in resolv.conf") \
_(DNSDomain, "dns-domain", NO_ALIAS, Kind::Value, L"Set the default DNS Domain") \
_(DNSOption, "dns-option", NO_ALIAS, Kind::Value, L"Set DNS options") \
_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, L"Set DNS search domains") \
_(Entrypoint, "entrypoint", NO_ALIAS, Kind::Value, L"Specifies the container init process executable") \
_(Env, "env", L"e", Kind::Value, L"Key=Value pairs for environment variables") \
_(EnvFile, "env-file", NO_ALIAS, Kind::Value, L"File containing key=value pairs of env variables") \
_(Format, "format", NO_ALIAS, Kind::Value, L"Output formatting (json or table) (Default:table)") \
_(ForwardArgs, "forwardargs", NO_ALIAS, Kind::Forward, L"Args to pass along") \
_(ForwardArgs, "arguments", NO_ALIAS, Kind::Forward, L"Arguments to pass to container's init process") \
_(GroupId, "groupid", NO_ALIAS, Kind::Value, L"Group Id for the process") \
_(Help, "help", WSLC_CLI_HELP_ARG, Kind::Flag, Localization::WSLCCLI_HelpArgDescription()) \
_(ImageId, "image", NO_ALIAS, Kind::Positional, L"Image name") \
_(Info, "info", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_InfoArgDescription()) \
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
_(Publish, "publish", L"p", Kind::Value, L"Publish port") \
_(Name, "name", NO_ALIAS, Kind::Value, L"Name of the container") \
_(NoDNS, "no-dns", NO_ALIAS, Kind::Flag, L"No configuration of DNS in the container") \
_(Progress, "progress", NO_ALIAS, Kind::Value, L"Progress type (format: none|ansi) (default: ansi)") \
_(Publish, "publish", L"p", Kind::Value, L"Publish a port from a container to host") \
_(Pull, "pull", NO_ALIAS, Kind::Value, L"Image pull policy (always|missing|never) (default:never)") \
_(Quiet, "quiet", L"q", Kind::Flag, L"Outputs the container IDs only") \
_(Remove, "remove", L"rm", Kind::Flag, L"Remove the container after execution") \
_(Remove, "remove", L"rm", Kind::Flag, L"Remove the container after it stops") \
_(Scheme, "scheme", NO_ALIAS, Kind::Value, L"Use this scheme for registry connection") \
_(SessionId, "session", NO_ALIAS, Kind::Value, Localization::WSLCCLI_SessionIdArgDescription()) \
_(Verbose, "verbose", L"v", Kind::Flag, L"Output verbose details")
_(Signal, "signal", L"s", Kind::Value, L"Signal to send (default: SIGKILL)") \
_(Time, "time", L"t", Kind::Value, L"Time in seconds to wait before executing (default 5)") \
_(TMPFS, "tmpfs", NO_ALIAS, Kind::Value, L"Mount tmpfs to the container at the given path") \
_(TTY, "tty", L"t", Kind::Flag, L"Open a TTY with the container process.") \
_(User, "user", L"u", Kind::Value, L"User ID for the process (name|uid|uid:gid)") \
_(Verbose, "verbose", L"v", Kind::Flag, L"Output verbose details") \
_(Virtual, "virtualization", NO_ALIAS, Kind::Value, L"Expose virtualization capabilities to the container") \
_(Volume, "volume", NO_ALIAS, Kind::Value, L"Bind mount a volume to the container") \
// clang-format on
29 changes: 10 additions & 19 deletions src/windows/wslc/arguments/ArgumentParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ ParseArgumentsStateMachine::State ParseArgumentsStateMachine::StepInternal()
// Assumes non-empty and does not begin with '-'.
ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessPositionalArgument(const std::wstring_view& currArg)
{
if (currArg.empty() || currArg[0] == WSLC_CLI_ARG_ID_CHAR)
{
// Assumption invalid, there is a bug in the logic.
THROW_HR(E_UNEXPECTED);
}
WI_ASSERT(!currArg.empty() && currArg[0] != WSLC_CLI_ARG_ID_CHAR);

const Argument* nextPositional = NextPositional();
if (!nextPositional)
Expand All @@ -170,14 +166,12 @@ ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessPositionalA
// Only Kind::Positional or Kind::Forward arguments should remain.
ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessAnchoredPositionals(const std::wstring_view& currArg)
{
if (!m_anchorPositional.has_value())
{
// Invalid state, this is a programmer error.
THROW_HR(E_UNEXPECTED);
}
WI_ASSERT(m_anchorPositional.has_value());

// If we haven't reached the limit for the anchor positional, treat this as another anchor positional.
if (m_executionArgs.Count(m_anchorPositional.value().Type()) < m_anchorPositional.value().Limit())
// Anchors with NO_LIMIT will never be full and therefore will always treat subsequent positionals as anchors.
if ((m_executionArgs.Count(m_anchorPositional.value().Type()) < m_anchorPositional.value().Limit()) ||
(m_anchorPositional.value().Limit() == NO_LIMIT))
{
// validate that we dont have any invalid argument specifiers.
if (!currArg.empty() && currArg[0] == WSLC_CLI_ARG_ID_CHAR)
Expand Down Expand Up @@ -219,10 +213,10 @@ ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessAnchoredPos
// currArg is the first forwarded argument
// All the rest of the args are forward args.
std::vector<std::wstring> forwardedArgs;
forwardedArgs.push_back(std::wstring{currArg});
forwardedArgs.emplace_back(std::wstring{currArg});
while (m_invocationItr != m_invocation.end())
{
forwardedArgs.push_back(std::wstring{*m_invocationItr});
forwardedArgs.emplace_back(std::wstring{*m_invocationItr});
++m_invocationItr;
}

Expand All @@ -233,11 +227,7 @@ ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessAnchoredPos
// Assumes argument begins with '-' and is at least 2 characters.
ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessAliasArgument(const std::wstring_view& currArg)
{
if (currArg.length() < 2 || currArg[0] != WSLC_CLI_ARG_ID_CHAR || currArg[1] == WSLC_CLI_ARG_ID_CHAR)
{
// Assumption invalid, this is a programmer error.
THROW_HR(E_UNEXPECTED);
}
WI_ASSERT(currArg.length() >= 2 && currArg[0] == WSLC_CLI_ARG_ID_CHAR && currArg[1] != WSLC_CLI_ARG_ID_CHAR);

// This may be a collection of boolean alias flags.
// Helper to find an argument by alias starting at a specific position.
Expand Down Expand Up @@ -338,7 +328,8 @@ ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessAliasArgume
// Assumes the arg value begins with -- and is at least 2 characters long.
ParseArgumentsStateMachine::State ParseArgumentsStateMachine::ProcessNamedArgument(const std::wstring_view& currArg)
{
THROW_HR_IF(E_UNEXPECTED, !currArg.starts_with(L"--"));
WI_ASSERT(currArg.starts_with(L"--"));

if (currArg.length() == 2)
{
// Missing argument name after double dash, this is an error.
Expand Down
70 changes: 70 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

ArgumentValidation.cpp

Abstract:

Implementation of the Argument Validation.

--*/
#include "Argument.h"
#include "ArgumentTypes.h"
#include "ArgumentValidation.h"
#include "ContainerModel.h"
#include "Exceptions.h"
#include <algorithm>
#include <charconv>

using namespace wsl::windows::common;

namespace wsl::windows::wslc {
// Common argument validation that occurs across multiple commands.
void Argument::Validate(const ArgMap& execArgs) const
{
switch (m_argType)
{
case ArgType::Signal:
validation::ValidateIntegerFromString<ULONG>(execArgs.GetAll<ArgType::Signal>(), m_name);
break;

case ArgType::Time:
validation::ValidateIntegerFromString<LONGLONG>(execArgs.GetAll<ArgType::Time>(), m_name);
break;

default:
break;
}
}
} // namespace wsl::windows::wslc

namespace wsl::windows::wslc::validation {

template <typename T>
void ValidateIntegerFromString(const std::vector<std::wstring>& values, const std::wstring& argName)
{
for (const auto& value : values)
{
std::ignore = GetIntegerFromString<T>(value, argName);
}
}

template <typename T>
T GetIntegerFromString(const std::wstring& value, const std::wstring& argName)
{
std::string narrowValue = string::WideToMultiByte(value);

T convertedValue{};
auto result = std::from_chars(narrowValue.c_str(), narrowValue.c_str() + narrowValue.size(), convertedValue);
if (result.ec != std::errc())
{
throw ArgumentException(L"Invalid " + argName + L" argument value: " + value);
}

return convertedValue;
}

} // namespace wsl::windows::wslc::validation
29 changes: 29 additions & 0 deletions src/windows/wslc/arguments/ArgumentValidation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

ArgumentValidation.h

Abstract:

Declaration of Argument Validation functions.

--*/
#pragma once
#include "ArgumentTypes.h"
#include "Exceptions.h"

using namespace wsl::windows::wslc;
using namespace wsl::windows::wslc::argument;

namespace wsl::windows::wslc::validation {

template <typename T>
void ValidateIntegerFromString(const std::vector<std::wstring>& values, const std::wstring& argName);

template <typename T>
T GetIntegerFromString(const std::wstring& value, const std::wstring& argName = {});

} // namespace wsl::windows::wslc::validation
6 changes: 5 additions & 1 deletion src/windows/wslc/commands/ContainerCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ namespace wsl::windows::wslc {
std::vector<std::unique_ptr<Command>> ContainerCommand::GetCommands() const
{
std::vector<std::unique_ptr<Command>> commands;
commands.reserve(1);
commands.reserve(5);
commands.push_back(std::make_unique<ContainerListCommand>(FullName()));
commands.push_back(std::make_unique<ContainerCreateCommand>(FullName()));
commands.push_back(std::make_unique<ContainerRunCommand>(FullName()));
commands.push_back(std::make_unique<ContainerStartCommand>(FullName()));
commands.push_back(std::make_unique<ContainerStopCommand>(FullName()));
return commands;
}

Expand Down
60 changes: 60 additions & 0 deletions src/windows/wslc/commands/ContainerCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ struct ContainerCommand final : public Command
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// Create Command
struct ContainerCreateCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"create";
ContainerCreateCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
std::vector<Argument> GetArguments() const override;
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

protected:
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// List Command
struct ContainerListCommand final : public Command
{
Expand All @@ -47,4 +62,49 @@ struct ContainerListCommand final : public Command
void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// Run Command
struct ContainerRunCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"run";
ContainerRunCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
std::vector<Argument> GetArguments() const override;
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

protected:
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// Start Command
struct ContainerStartCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"start";
ContainerStartCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
std::vector<Argument> GetArguments() const override;
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

protected:
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// Stop Command
struct ContainerStopCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"stop";
ContainerStopCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
std::vector<Argument> GetArguments() const override;
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

protected:
void ExecuteInternal(CLIExecutionContext& context) const override;
};
} // namespace wsl::windows::wslc
Loading