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
26 changes: 26 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3674,6 +3674,12 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_TableHeaderName" xml:space="preserve">
<value>NAME</value>
</data>
<data name="WSLCCLI_TableHeaderNames" xml:space="preserve">
<value>NAMES</value>
</data>
<data name="WSLCCLI_TableHeaderCommand" xml:space="preserve">
<value>COMMAND</value>
</data>
<data name="WSLCCLI_TableHeaderImage" xml:space="preserve">
<value>IMAGE</value>
</data>
Expand Down Expand Up @@ -3704,6 +3710,26 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_TableHeaderPids" xml:space="preserve">
<value>PIDS</value>
</data>
<data name="WSLCCLI_ContainerStateCreated" xml:space="preserve">
<value>created</value>
<comment>Container state shown in the STATUS column of `wslc container list`.</comment>
</data>
<data name="WSLCCLI_ContainerStateRunning" xml:space="preserve">
<value>running</value>
<comment>Container state shown in the STATUS column of `wslc container list`.</comment>
</data>
<data name="WSLCCLI_ContainerStateStopped" xml:space="preserve">
<value>stopped</value>
<comment>Container state shown in the STATUS column of `wslc container list`.</comment>
</data>
<data name="WSLCCLI_ContainerStateExited" xml:space="preserve">
<value>exited</value>
<comment>Container state shown in the STATUS column of `wslc container list`.</comment>
</data>
<data name="WSLCCLI_ContainerStateInvalid" xml:space="preserve">
<value>invalid</value>
<comment>Container state shown in the STATUS column of `wslc container list`.</comment>
</data>
<data name="WSLCCLI_RelativeTimeLessThanASecond" xml:space="preserve">
<value>Less than a second ago</value>
<comment>Describes how long ago something happened, shown in the CREATED column of 'wslc container list' and 'wslc image list'.</comment>
Expand Down
1 change: 1 addition & 0 deletions src/windows/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ set(HEADERS
VTSupport.h
WindowsUpdateIntegration.h
WSLCContainerLauncher.h
WSLCContainerEntry.h
ConsommeNetworking.h
WSLCProcessLauncher.h
WslClient.h
Expand Down
28 changes: 28 additions & 0 deletions src/windows/common/WSLCContainerEntry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (C) Microsoft Corporation. All rights reserved.

#pragma once

#include <wil/resource.h>
#include "wslc.h"

namespace wsl::windows::common::wslc {

// IWSLCSession::ListContainers allocates a string for every unbounded field on an entry, so each
// element owns memory that releasing the array alone would miss.
struct ContainerEntryDeleter
{
void operator()(WSLCContainerEntry& Entry) const
{
CoTaskMemFree(Entry.Command);
CoTaskMemFree(Entry.Status);
CoTaskMemFree(Entry.Labels);
CoTaskMemFree(Entry.Networks);
CoTaskMemFree(Entry.Mounts);
}
};

// Owns both the entry array and the per-entry strings. Callers of ListContainers should use this
// rather than a plain cotaskmem array so the strings cannot be leaked.
using unique_container_entry_array = wil::unique_any_array_ptr<WSLCContainerEntry, wil::cotaskmem_deleter, ContainerEntryDeleter>;

} // namespace wsl::windows::common::wslc
51 changes: 51 additions & 0 deletions src/windows/common/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,57 @@ std::string wsl::windows::common::string::TruncateId(_In_ std::string_view id, b
return TruncateIdImpl(id, shortenLength);
}

// Returns the number of terminal columns a code point occupies. This mirrors docker's charWidth, which treats
// East Asian wide and fullwidth code points as two columns and everything else as one.
static size_t CharacterWidth(UChar32 CodePoint)
{
const auto width = u_getIntPropertyValue(CodePoint, UCHAR_EAST_ASIAN_WIDTH);
return (width == U_EA_WIDE || width == U_EA_FULLWIDTH) ? 2 : 1;
}

std::wstring wsl::windows::common::string::Ellipsis(_In_ std::wstring_view Value, _In_ size_t MaxDisplayWidth)
{
if (MaxDisplayWidth == 0 || Value.empty())
{
return {};
}

const auto length = gsl::narrow_cast<int32_t>(Value.size());
if (MaxDisplayWidth == 1)
{
// There is no room for both content and an ellipsis, so the leading code point is kept as-is even
// if it is wider than the limit.
int32_t index = 0;
UChar32 codePoint{};
U16_NEXT(Value.data(), index, length, codePoint);
return std::wstring{Value.substr(0, index)};
}

// The ellipsis occupies one column, so the retained content has one column less to work with.
const auto budget = MaxDisplayWidth - 1;
size_t totalWidth = 0;
size_t cutoff = 0;
for (int32_t index = 0; index < length;)
{
UChar32 codePoint{};
U16_NEXT(Value.data(), index, length, codePoint);
totalWidth += CharacterWidth(codePoint);
if (totalWidth <= budget)
{
cutoff = index;
}
}

// A cutoff of zero means the first code point alone leaves no room for the ellipsis, in which case docker
// returns the value untouched.
if (totalWidth <= MaxDisplayWidth || cutoff == 0)
{
return std::wstring{Value};
}

return std::wstring{Value.substr(0, cutoff)} + L'\u2026';
}

std::uint64_t wsl::windows::common::string::Rfc3339ToEpoch(const std::string& timestamp)
{
std::chrono::sys_seconds utcSeconds;
Expand Down
6 changes: 6 additions & 0 deletions src/windows/common/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ std::string WideToMultiByte(_In_ std::wstring_view Source);
std::wstring TruncateId(_In_ std::wstring_view id, bool shortenLength = true);
std::string TruncateId(_In_ std::string_view id, bool shortenLength = true);

// Shortens a value so it occupies at most MaxDisplayWidth terminal columns, appending an ellipsis when
// characters are dropped. East Asian wide and fullwidth code points occupy two columns, so fewer of them
// fit than narrow ones, and a code point is never split. This matches docker's formatter.Ellipsis
// (cli/command/formatter/displayutils.go), including its handling of widths of one and below.
std::wstring Ellipsis(_In_ std::wstring_view Value, _In_ size_t MaxDisplayWidth);

// Converts an RFC 3339 timestamp to seconds since the unix epoch. Only the 'Z' zone designator is
// accepted; numeric offsets are not.
std::uint64_t Rfc3339ToEpoch(const std::string& timestamp);
Expand Down
5 changes: 4 additions & 1 deletion src/windows/inc/docker_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,8 @@ struct ContainerInfo
std::vector<std::string> Names;
std::string Image;
std::string ImageID;
std::string Command;
std::string Status;
std::map<std::string, std::string> Labels;
std::vector<Port> Ports;
std::vector<Mount> Mounts;
Expand All @@ -779,7 +781,8 @@ struct ContainerInfo
HostConfig HostConfig;
NetworkSettings NetworkSettings;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ContainerInfo, Id, Names, Image, ImageID, Labels, Ports, Mounts, State, Created, HostConfig, NetworkSettings);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
ContainerInfo, Id, Names, Image, ImageID, Command, Status, Labels, Ports, Mounts, State, Created, HostConfig, NetworkSettings);
};

struct BuildKitVertex
Expand Down
8 changes: 8 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,17 @@ typedef struct _WSLCContainerEntry
{
char Name[WSLC_MAX_CONTAINER_NAME_LENGTH + 1];
char Image[WSLC_MAX_IMAGE_NAME_LENGTH + 1];
// The runtime imposes no bound on these values, so they are allocated by the callee and freed
// by the caller. Any of them may be null when the container reports no value.
[string] LPSTR Command;
[string] LPSTR Status;
[string] LPSTR Labels;
[string] LPSTR Networks;
[string] LPSTR Mounts;
WSLCContainerId Id;
ULONGLONG StateChangedAt;
ULONGLONG CreatedAt;
ULONG LocalVolumes;
WSLCContainerState State;
} WSLCContainerEntry;

Expand Down
40 changes: 39 additions & 1 deletion src/windows/wslc/services/ContainerModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,50 @@ struct ContainerInformation
std::string Id;
std::string Name;
std::string Image;
// Command and runtime supplied status description.
std::string Command;
std::string Status;
std::string Labels;
std::string Networks;
std::string Mounts;
ULONG LocalVolumes{};
WSLCContainerState State;
ULONGLONG StateChangedAt{};
ULONGLONG CreatedAt{};
std::vector<PortInformation> Ports;
};

// The platform a container runs on. Emitted as a nested object to match docker.
struct ContainerPlatform
{
std::string architecture;
std::string os;

NLOHMANN_DEFINE_TYPE_INTRUSIVE(ContainerInformation, Id, Name, Image, State, StateChangedAt, CreatedAt, Ports);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ContainerPlatform, architecture, os);
};

// The shape emitted by "container list --format json".
struct ContainerOutputInformation
{
std::string Command;
std::string CreatedAt;
std::string HealthStatus;
std::string ID;
std::string Image;
std::string Labels;
std::string LocalVolumes;
std::string Mounts;
std::string Names;
std::string Networks;
ContainerPlatform Platform;
std::string Ports;
std::string RunningFor;
std::string Size;
std::string State;
std::string Status;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
ContainerOutputInformation, Command, CreatedAt, HealthStatus, ID, Image, Labels, LocalVolumes, Mounts, Names, Networks, Platform, Ports, RunningFor, Size, State, Status);
};

struct EnvironmentVariable
Expand Down
Loading
Loading