master
cpp 139 lines 4.63 KB
Raw
1 // WSLC-Neofetch
2 //
3 // A Windows executable that wraps the Linux "neofetch" command using the
4 // WSL Container SDK, written with the modern C++/WinRT projection
5 // (winrt/Microsoft.WSL.Containers.h) rather than the flat C API.
6 //
7 // All command-line arguments are forwarded into the container, so
8 // "neofetch.exe --help" behaves just like "neofetch --help" on Linux.
9
10 #include <winsock2.h>
11 #include <windows.h>
12 #include <stdio.h>
13 #include <string>
14 #include <vector>
15
16 #include <winrt/Windows.Foundation.h>
17 #include <winrt/Windows.Foundation.Collections.h>
18 #include <winrt/Microsoft.WSL.Containers.h>
19
20 #pragma comment(lib, "windowsapp.lib")
21
22 using namespace winrt;
23 using namespace winrt::Microsoft::WSL::Containers;
24
25 namespace {
26 constexpr std::wstring_view c_imageName = L"anrginit/ubuntu-neofetch:1.0";
27
28 // Forward a chunk of container stdout/stderr straight to the Windows console.
29 void WriteToConsole(FILE* stream, array_view<uint8_t const> data)
30 {
31 fprintf(stream, "%.*s", static_cast<int>(data.size()), reinterpret_cast<const char*>(data.data()));
32 fflush(stream);
33 }
34
35 // Build a storage path in a "WslcStorage" folder next to the executable, so
36 // the sample doesn't depend on any hard-coded absolute path.
37 std::wstring GetStoragePath()
38 {
39 wchar_t exePath[MAX_PATH];
40 GetModuleFileNameW(nullptr, exePath, MAX_PATH);
41 wchar_t* lastSlash = wcsrchr(exePath, L'\\');
42 if (lastSlash != nullptr)
43 {
44 *(lastSlash + 1) = L'\0';
45 }
46 return std::wstring{exePath} + L"WslcStorage";
47 }
48 } // namespace
49
50 int wmain(int argc, wchar_t* argv[])
51 {
52 init_apartment();
53
54 // argv[0] (our exe) is replaced with "neofetch"; the rest pass through.
55 std::vector<hstring> commandLine{L"neofetch"};
56 for (int i = 1; i < argc; ++i)
57 {
58 commandLine.emplace_back(argv[i]);
59 }
60
61 try
62 {
63 // ---- Session ----
64 fwprintf(stderr, L"[wslc] Creating session...\n");
65 SessionSettings sessionSettings{L"WSLCNeofetch", GetStoragePath()};
66 sessionSettings.CpuCount(4);
67 sessionSettings.MemorySizeInMB(2048);
68
69 Session session{sessionSettings};
70 session.Start();
71
72 // ---- Pull image ----
73 fwprintf(stderr, L"[wslc] Pulling image '%ls'...\n", c_imageName.data());
74 session.PullImage(PullImageOptions{hstring{c_imageName}});
75
76 // ---- Create & start container ----
77 // The init process keeps the container alive while we exec neofetch.
78 fwprintf(stderr, L"[wslc] Starting container...\n");
79 ProcessSettings initProcess;
80 initProcess.CommandLine(single_threaded_vector<hstring>({L"/bin/sleep", L"60"}));
81
82 ContainerSettings containerSettings{hstring{c_imageName}};
83 containerSettings.Name(L"wslc-neofetch");
84 containerSettings.InitProcess(initProcess);
85 containerSettings.EnableAutoRemove(true);
86
87 Container container = session.CreateContainer(containerSettings);
88 container.Start();
89
90 // ---- Exec neofetch ----
91 fwprintf(stderr, L"[wslc] Running neofetch...\n");
92 ProcessSettings processSettings;
93 processSettings.CommandLine(single_threaded_vector<hstring>(std::move(commandLine)));
94 processSettings.OutputMode(ProcessOutputMode::Event);
95
96 Process process = container.CreateProcess(processSettings);
97
98 handle exitEvent{CreateEvent(nullptr, TRUE, FALSE, nullptr)};
99 if (!exitEvent)
100 {
101 throw_last_error();
102 }
103 int32_t exitCode = -1;
104
105 process.OutputReceived([](array_view<uint8_t const> data) { WriteToConsole(stdout, data); });
106 process.ErrorReceived([](array_view<uint8_t const> data) { WriteToConsole(stderr, data); });
107 process.Exited([&](int32_t code) {
108 exitCode = code;
109 if (!SetEvent(exitEvent.get()))
110 {
111 fwprintf(stderr, L"[wslc] Warning: SetEvent failed (0x%08X)\n", GetLastError());
112 }
113 });
114
115 process.Start();
116 DWORD waitResult = WaitForSingleObject(exitEvent.get(), 30000);
117 if (waitResult == WAIT_TIMEOUT)
118 {
119 fwprintf(stderr, L"[wslc] Error: Timed out waiting for the process to exit.\n");
120 }
121 else if (waitResult != WAIT_OBJECT_0)
122 {
123 throw_last_error();
124 }
125
126 // ---- Cleanup ----
127 fwprintf(stderr, L"[wslc] Shutting down...\n");
128 container.Stop(Signal::SIGTERM, std::chrono::seconds{5});
129 session.Terminate();
130
131 fwprintf(stderr, L"[wslc] Done.\n");
132 return exitCode;
133 }
134 catch (hresult_error const& ex)
135 {
136 fwprintf(stderr, L"[wslc] Error: %ls (0x%08X)\n", ex.message().c_str(), static_cast<uint32_t>(ex.code()));
137 return 1;
138 }
139 }