master
c 214 lines 6.1 KB
Raw
1 // WSLC-HelloWorld
2 //
3 // The simplest possible WSL Container SDK sample, written in C using the flat
4 // C API (wslcsdk.h). It starts a lightweight WSL container from a small Linux
5 // image and runs `echo` inside it, streaming the output back to the Windows
6 // console.
7
8 #include <winsock2.h>
9 #include <windows.h>
10 #include <stdio.h>
11 #include <objbase.h>
12 #include "wslcsdk.h"
13
14 #pragma comment(lib, "ole32.lib")
15 #pragma comment(lib, "wslcsdk.lib")
16
17 static const char* IMAGE_NAME = "alpine:latest";
18
19 // Process exit is signalled here so wmain can wait for it.
20 static HANDLE g_exitEvent = NULL;
21 static INT32 g_exitCode = -1;
22
23 static void PrintError(const wchar_t* context, HRESULT hr, PWSTR error)
24 {
25 fwprintf(stderr, L"[wslc] Error: %s (0x%08X)", context, hr);
26 if (error != NULL)
27 {
28 fwprintf(stderr, L": %s", error);
29 CoTaskMemFree(error);
30 }
31 fwprintf(stderr, L"\n");
32 }
33
34 // Forward container stdout/stderr straight to the Windows console.
35 static void CALLBACK OnStdIO(WslcProcessIOHandle ioHandle, const BYTE* data, uint32_t dataSize, PVOID context)
36 {
37 FILE* output = (ioHandle == WSLC_PROCESS_IO_HANDLE_STDOUT) ? stdout : stderr;
38 (void)context;
39 fprintf(output, "%.*s", (int)dataSize, (const char*)data);
40 fflush(output);
41 }
42
43 // Record the exit code and wake up wmain.
44 static void CALLBACK OnProcessExit(INT32 exitCode, PVOID context)
45 {
46 (void)context;
47 g_exitCode = exitCode;
48 if (!SetEvent(g_exitEvent))
49 {
50 fwprintf(stderr, L"[wslc] Warning: SetEvent failed (0x%08X)\n", GetLastError());
51 }
52 }
53
54 // Build a storage path in a "WslcStorage" folder next to the executable, so the
55 // sample doesn't depend on any hard-coded absolute path.
56 static void GetStoragePath(wchar_t* buffer, size_t count)
57 {
58 wchar_t exePath[MAX_PATH];
59 wchar_t* lastSlash;
60 GetModuleFileNameW(NULL, exePath, MAX_PATH);
61 lastSlash = wcsrchr(exePath, L'\\');
62 if (lastSlash != NULL)
63 {
64 *(lastSlash + 1) = L'\0';
65 }
66 swprintf(buffer, count, L"%sWslcStorage", exePath);
67 }
68
69 int wmain(void)
70 {
71 HRESULT hr;
72 PWSTR error = NULL;
73 int result = 1;
74
75 WslcSession session = NULL;
76 WslcContainer container = NULL;
77 WslcProcess process = NULL;
78
79 WslcSessionSettings sessionSettings;
80 WslcContainerSettings containerSettings;
81 WslcProcessSettings initProcess;
82 WslcProcessSettings execProcess;
83 WslcProcessCallbacks callbacks;
84 WslcPullImageOptions pullOptions;
85 wchar_t storagePath[MAX_PATH];
86 DWORD waitResult;
87
88 PCSTR initArgv[2] = {"/bin/sleep", "60"};
89 PCSTR echoArgv[2] = {"/bin/echo", "Hello, World from a WSL container!"};
90
91 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
92 if (FAILED(hr))
93 {
94 PrintError(L"Initialize COM", hr, NULL);
95 return 1;
96 }
97
98 g_exitEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
99 if (g_exitEvent == NULL)
100 {
101 PrintError(L"Create exit event", HRESULT_FROM_WIN32(GetLastError()), NULL);
102 goto cleanup;
103 }
104
105 // ---- Session ----
106 fwprintf(stderr, L"[wslc] Creating session...\n");
107 GetStoragePath(storagePath, ARRAYSIZE(storagePath));
108 hr = WslcInitSessionSettings(L"WSLCHelloWorld", storagePath, &sessionSettings);
109 if (FAILED(hr))
110 {
111 PrintError(L"Init session settings", hr, NULL);
112 goto cleanup;
113 }
114
115 hr = WslcCreateSession(&sessionSettings, &session, &error);
116 if (FAILED(hr))
117 {
118 PrintError(L"Create session", hr, error);
119 goto cleanup;
120 }
121
122 // ---- Pull image ----
123 fwprintf(stderr, L"[wslc] Pulling image '%hs'...\n", IMAGE_NAME);
124 ZeroMemory(&pullOptions, sizeof(pullOptions));
125 pullOptions.uri = IMAGE_NAME;
126 hr = WslcPullSessionImage(session, &pullOptions, &error);
127 if (FAILED(hr))
128 {
129 PrintError(L"Pull image", hr, error);
130 goto cleanup;
131 }
132
133 // ---- Create & start container ----
134 fwprintf(stderr, L"[wslc] Starting container...\n");
135 WslcInitProcessSettings(&initProcess);
136 WslcSetProcessSettingsCmdLine(&initProcess, initArgv, 2);
137
138 WslcInitContainerSettings(IMAGE_NAME, &containerSettings);
139 WslcSetContainerSettingsName(&containerSettings, "wslc-helloworld");
140 WslcSetContainerSettingsInitProcess(&containerSettings, &initProcess);
141 WslcSetContainerSettingsFlags(&containerSettings, WSLC_CONTAINER_FLAG_AUTO_REMOVE);
142
143 hr = WslcCreateContainer(session, &containerSettings, &container, &error);
144 if (FAILED(hr))
145 {
146 PrintError(L"Create container", hr, error);
147 goto cleanup;
148 }
149
150 hr = WslcStartContainer(container, WSLC_CONTAINER_START_FLAG_NONE, &error);
151 if (FAILED(hr))
152 {
153 PrintError(L"Start container", hr, error);
154 goto cleanup;
155 }
156
157 // ---- Run echo ----
158 fwprintf(stderr, L"[wslc] Running echo...\n");
159 WslcInitProcessSettings(&execProcess);
160 WslcSetProcessSettingsCmdLine(&execProcess, echoArgv, 2);
161
162 ZeroMemory(&callbacks, sizeof(callbacks));
163 callbacks.onStdOut = OnStdIO;
164 callbacks.onStdErr = OnStdIO;
165 callbacks.onExit = OnProcessExit;
166 WslcSetProcessSettingsCallbacks(&execProcess, &callbacks, NULL);
167
168 hr = WslcCreateContainerProcess(container, &execProcess, &process, &error);
169 if (FAILED(hr))
170 {
171 PrintError(L"Run echo", hr, error);
172 goto cleanup;
173 }
174
175 waitResult = WaitForSingleObject(g_exitEvent, 30000);
176 if (waitResult == WAIT_OBJECT_0)
177 {
178 result = g_exitCode;
179 }
180 else if (waitResult == WAIT_TIMEOUT)
181 {
182 fwprintf(stderr, L"[wslc] Error: Timed out waiting for the process to exit.\n");
183 }
184 else
185 {
186 PrintError(L"Wait for process exit", HRESULT_FROM_WIN32(GetLastError()), NULL);
187 }
188
189 cleanup:
190 fwprintf(stderr, L"[wslc] Shutting down...\n");
191
192 if (process != NULL)
193 {
194 WslcReleaseProcess(process);
195 }
196 if (g_exitEvent != NULL)
197 {
198 CloseHandle(g_exitEvent);
199 }
200 if (container != NULL)
201 {
202 WslcStopContainer(container, WSLC_SIGNAL_SIGTERM, 5, NULL);
203 WslcReleaseContainer(container);
204 }
205 if (session != NULL)
206 {
207 WslcTerminateSession(session);
208 WslcReleaseSession(session);
209 }
210
211 fwprintf(stderr, L"[wslc] Done.\n");
212 CoUninitialize();
213 return result;
214 }