1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3
4use windows::core::{IUnknown, Interface, Result, GUID, HRESULT, PCSTR, PCWSTR, PWSTR};
5use windows::Win32::{
6 Foundation::HANDLE,
7 System::Com::{CoCreateInstance, CLSCTX_LOCAL_SERVER},
8};
9
10use std::mem::MaybeUninit;
11
12pub mod constants;
13pub mod error;
14pub mod interop;
15
16const CLSID_LXSSUSERSESSION: GUID = GUID::from_u128(0xa9b7a1b9_0671_405c_95f1_e0612cb4ce7e);
17const IID_ILXSSUSERSESSION: GUID = GUID::from_u128(0x38541bdc_f54f_4ceb_85d0_37f0f3d2617e);
18
19pub type LxssError = (HRESULT, LXSS_ERROR_INFO);
20pub type LxssResult<T> = std::result::Result<T, LxssError>;
21
22#[repr(C)]
23pub struct LXSS_ERROR_INFO {
24 pub Flags: u32,
25 pub Context: u64,
26 pub Message: PWSTR,
27 pub Warnings: PWSTR,
28 pub WarningsPipe: u32,
29}
30
31unsafe impl Send for LXSS_ERROR_INFO {}
32
33impl std::fmt::Debug for LXSS_ERROR_INFO {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 let mut s = f.debug_struct("LXSS_ERROR_INFO");
36 s.field("Flags", &self.Flags)
37 .field("Context", &self.Context)
38 .field("WarningsPipe", &self.WarningsPipe);
39
40 if !self.Message.is_null() {
41 s.field("Message", &unsafe { self.Message.to_string() });
42 }
43
44 if !self.Warnings.is_null() {
45 s.field("Warnings", &unsafe { self.Warnings.to_string() });
46 }
47
48 s.finish()
49 }
50}
51
52#[repr(C)]
53pub struct LXSS_ENUMERATE_INFO {
54 pub DistroGuid: GUID,
55 pub State: u32,
56 pub Version: u32,
57 pub Flags: u32,
58 pub DistroName: [u16; 257],
59}
60
61#[repr(C)]
62pub struct LXSS_HANDLE {
63 pub Handle: u32,
64 pub HandleType: LxssHandleType,
65}
66
67#[repr(u32)]
68pub enum LxssHandleType {
69 LxssHandleConsole = 0,
70 LxssHandleInput,
71 LxssHandleOutput,
72}
73
74#[repr(C)]
75pub struct LXSS_STD_HANDLES {
76 pub StdIn: LXSS_HANDLE,
77 pub StdOut: LXSS_HANDLE,
78 pub StdErr: LXSS_HANDLE,
79}
80
81#[repr(transparent)]
82#[derive(Clone)]
83pub struct ILxssUserSession(pub IUnknown);
84
85unsafe impl Interface for ILxssUserSession {
86 type Vtable = ILxssUserSession_Vtbl;
87 const IID: GUID = IID_ILXSSUSERSESSION;
88}
89
90#[repr(C)]
91pub struct ILxssUserSession_Vtbl {
92 pub base__: windows::core::IUnknown_Vtbl,
93
94 pub CreateInstance: unsafe extern "system" fn(
95 this: *mut ::core::ffi::c_void,
96 distro_guid: *const GUID,
97 flags: u32,
98 error: *mut LXSS_ERROR_INFO,
99 ) -> HRESULT,
100
101 pub RegisterDistribution: unsafe extern "system" fn(
102 this: *mut ::core::ffi::c_void,
103 name: PCWSTR,
104 version: u32,
105 file_handle: HANDLE,
106 stderr_handle: HANDLE,
107 target_directory: PCWSTR,
108 flags: u32,
109 vhd_size: u64,
110 package_family_name: PCWSTR,
111 installed_name: *mut PWSTR,
112 error: *mut LXSS_ERROR_INFO,
113 out_guid: *mut GUID,
114 ) -> HRESULT,
115
116 pub RegisterDistributionPipe: unsafe extern "system" fn(
117 this: *mut ::core::ffi::c_void,
118 name: PCWSTR,
119 version: u32,
120 pipe_handle: HANDLE,
121 stderr_handle: HANDLE,
122 target_directory: PCWSTR,
123 flags: u32,
124 vhd_size: u64,
125 package_family_name: PCWSTR,
126 installed_name: *mut PWSTR,
127 error: *mut LXSS_ERROR_INFO,
128 out_guid: *mut GUID,
129 ) -> HRESULT,
130
131 pub GetDistributionId: unsafe extern "system" fn(
132 this: *mut ::core::ffi::c_void,
133 name: PCWSTR,
134 flags: u32,
135 error: *mut LXSS_ERROR_INFO,
136 out_guid: *mut GUID,
137 ) -> HRESULT,
138
139 pub TerminateDistribution: unsafe extern "system" fn(
140 this: *mut ::core::ffi::c_void,
141 distro_guid: *const GUID,
142 error: *mut LXSS_ERROR_INFO,
143 ) -> HRESULT,
144
145 pub UnregisterDistribution: unsafe extern "system" fn(
146 this: *mut ::core::ffi::c_void,
147 distro_guid: *const GUID,
148 error: *mut LXSS_ERROR_INFO,
149 ) -> HRESULT,
150
151 pub ConfigureDistribution: unsafe extern "system" fn(
152 this: *mut ::core::ffi::c_void,
153 distro_guid: *const GUID,
154 default_uid: u32,
155 flags: u32,
156 error: *mut LXSS_ERROR_INFO,
157 ) -> HRESULT,
158
159 pub GetDistributionConfiguration: unsafe extern "system" fn(
160 this: *mut ::core::ffi::c_void,
161 distro_guid: *const GUID,
162 distribution_name: *mut PWSTR,
163 version: *mut u32,
164 default_uid: *mut u32,
165 env_count: *mut u32,
166 default_environment: *mut *mut PCSTR,
167 flags: *mut u32,
168 error: *mut LXSS_ERROR_INFO,
169 ) -> HRESULT,
170
171 pub GetDefaultDistribution: unsafe extern "system" fn(
172 this: *mut ::core::ffi::c_void,
173 error: *mut LXSS_ERROR_INFO,
174 out_guid: *mut GUID,
175 ) -> HRESULT,
176
177 pub ResizeDistribution: unsafe extern "system" fn(
178 this: *mut ::core::ffi::c_void,
179 distro_guid: *const GUID,
180 output_handle: HANDLE,
181 new_size: u64,
182 error: *mut LXSS_ERROR_INFO,
183 ) -> HRESULT,
184
185 pub SetDefaultDistribution: unsafe extern "system" fn(
186 this: *mut ::core::ffi::c_void,
187 distro_guid: *const GUID,
188 error: *mut LXSS_ERROR_INFO,
189 ) -> HRESULT,
190
191 pub SetSparse: unsafe extern "system" fn(
192 this: *mut ::core::ffi::c_void,
193 distro_guid: *const GUID,
194 sparse: i32,
195 allow_unsafe: i32,
196 error: *mut LXSS_ERROR_INFO,
197 ) -> HRESULT,
198
199 pub EnumerateDistributions: unsafe extern "system" fn(
200 this: *mut ::core::ffi::c_void,
201 count: *mut u32,
202 distros: *mut *const LXSS_ENUMERATE_INFO,
203 error: *mut LXSS_ERROR_INFO,
204 ) -> HRESULT,
205
206 pub CreateLxProcess: unsafe extern "system" fn(
207 this: *mut ::core::ffi::c_void,
208 distro_guid: *const GUID,
209 filename: PCSTR,
210 command_line_count: u32,
211 command_line: *const PCSTR,
212 cwd: PCWSTR,
213 nt_path: PCWSTR,
214 nt_env: *mut u16,
215 nt_env_len: u32,
216 username: PCWSTR,
217 columns: i16,
218 rows: i16,
219 console_handle: u32,
220 std_handles: *const LXSS_STD_HANDLES,
221 flags: u32,
222 out_distribution_id: *mut GUID,
223 out_instance_id: *mut GUID,
224 process_handle: *mut HANDLE,
225 server_handle: *mut HANDLE,
226 stdin: *mut HANDLE,
227 stdout: *mut HANDLE,
228 stderr: *mut HANDLE,
229 comm_channel: *mut HANDLE,
230 interop_socket: *mut HANDLE,
231 error: *mut LXSS_ERROR_INFO,
232 ) -> HRESULT,
233
234 pub SetVersion: unsafe extern "system" fn(
235 this: *mut ::core::ffi::c_void,
236 distro_guid: *const GUID,
237 version: u32,
238 stderr_handle: HANDLE,
239 error: *mut LXSS_ERROR_INFO,
240 ) -> HRESULT,
241
242 pub ExportDistribution: unsafe extern "system" fn(
243 this: *mut ::core::ffi::c_void,
244 distro_guid: *const GUID,
245 file_handle: HANDLE,
246 stderr_handle: HANDLE,
247 flags: u32,
248 error: *mut LXSS_ERROR_INFO,
249 ) -> HRESULT,
250
251 pub ExportDistributionPipe: unsafe extern "system" fn(
252 this: *mut ::core::ffi::c_void,
253 distro_guid: *const GUID,
254 pipe_handle: HANDLE,
255 stderr_handle: HANDLE,
256 flags: u32,
257 error: *mut LXSS_ERROR_INFO,
258 ) -> HRESULT,
259
260 pub AttachDisk: unsafe extern "system" fn(
261 this: *mut ::core::ffi::c_void,
262 disk: PCWSTR,
263 flags: u32,
264 error: *mut LXSS_ERROR_INFO,
265 ) -> HRESULT,
266
267 pub DetachDisk: unsafe extern "system" fn(
268 this: *mut ::core::ffi::c_void,
269 disk: PCWSTR,
270 result: *mut i32,
271 step: *mut i32,
272 error: *mut LXSS_ERROR_INFO,
273 ) -> HRESULT,
274
275 pub MountDisk: unsafe extern "system" fn(
276 this: *mut ::core::ffi::c_void,
277 disk: PCWSTR,
278 flags: u32,
279 partition_index: u32,
280 name: PCWSTR,
281 ttype: PCWSTR,
282 options: PCWSTR,
283 result: *mut i32,
284 step: *mut i32,
285 mount_name: *mut PWSTR,
286 error: *mut LXSS_ERROR_INFO,
287 ) -> HRESULT,
288
289 pub Shutdown: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, force: i32) -> HRESULT,
290
291 pub ImportDistributionInplace: unsafe extern "system" fn(
292 this: *mut ::core::ffi::c_void,
293 name: PCWSTR,
294 vhd_path: PCWSTR,
295 error: *mut LXSS_ERROR_INFO,
296 out_guid: *mut GUID,
297 ) -> HRESULT,
298
299 pub MoveDistribution: unsafe extern "system" fn(
300 this: *mut ::core::ffi::c_void,
301 distro_guid: *const GUID,
302 name: PCWSTR,
303 error: *mut LXSS_ERROR_INFO,
304 ) -> HRESULT,
305}
306
307pub unsafe fn get_lxss_user_session() -> windows::core::Result<ILxssUserSession> {
308 let session: ILxssUserSession =
309 unsafe { CoCreateInstance(&CLSID_LXSSUSERSESSION, None, CLSCTX_LOCAL_SERVER)? };
310
311 Ok(session)
312}
313
314impl ILxssUserSession {
315 pub unsafe fn CreateInstance(&self, distro_guid: GUID, flags: u32) -> LxssResult<()> {
316 unsafe {
317 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
318 let mut error_info = std::mem::zeroed();
319 let result = ((*vtable).CreateInstance)(
320 self.0.as_raw(),
321 std::ptr::from_ref(&distro_guid),
322 flags,
323 std::ptr::from_mut(&mut error_info),
324 );
325 if result.is_ok() {
326 Ok(())
327 } else {
328 Err((result, error_info))
329 }
330 }
331 }
332
333 pub unsafe fn GetDefaultDistribution(&self) -> LxssResult<GUID> {
334 unsafe {
335 let vtable = self.vtable();
336 let mut error_info: LXSS_ERROR_INFO = std::mem::zeroed();
337 let mut guid = MaybeUninit::uninit();
338 let result = ((*vtable).GetDefaultDistribution)(
339 self.0.as_raw(),
340 std::ptr::from_mut(&mut error_info),
341 guid.as_mut_ptr(),
342 );
343 if result.is_ok() {
344 Ok(guid.assume_init())
345 } else {
346 Err((result, error_info))
347 }
348 }
349 }
350
351 pub unsafe fn UnregisterDistribution(&self, distro_guid: GUID) -> LxssResult<()> {
352 unsafe {
353 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
354 let mut error_info = std::mem::zeroed();
355 let result = ((*vtable).UnregisterDistribution)(
356 self.0.as_raw(),
357 std::ptr::from_ref(&distro_guid),
358 std::ptr::from_mut(&mut error_info),
359 );
360 if result.is_ok() {
361 Ok(())
362 } else {
363 Err((result, error_info))
364 }
365 }
366 }
367
368 pub unsafe fn TerminateDistribution(&self, distro_guid: GUID) -> LxssResult<()> {
369 unsafe {
370 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
371 let mut error_info = std::mem::zeroed();
372 let result = ((*vtable).TerminateDistribution)(
373 self.0.as_raw(),
374 std::ptr::from_ref(&distro_guid),
375 std::ptr::from_mut(&mut error_info),
376 );
377 if result.is_ok() {
378 Ok(())
379 } else {
380 Err((result, error_info))
381 }
382 }
383 }
384
385 pub unsafe fn ConfigureDistribution(
386 &self,
387 distro_guid: GUID,
388 default_uid: u32,
389 flags: u32,
390 ) -> LxssResult<()> {
391 unsafe {
392 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
393 let mut error_info = std::mem::zeroed();
394 let result = ((*vtable).ConfigureDistribution)(
395 self.0.as_raw(),
396 std::ptr::from_ref(&distro_guid),
397 default_uid,
398 flags,
399 std::ptr::from_mut(&mut error_info),
400 );
401 if result.is_ok() {
402 Ok(())
403 } else {
404 Err((result, error_info))
405 }
406 }
407 }
408
409 pub unsafe fn EnumerateDistributions(&self) -> LxssResult<(u32, *const LXSS_ENUMERATE_INFO)> {
410 unsafe {
411 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
412 let mut error_info = std::mem::zeroed();
413 let mut distros = std::ptr::null();
414 let mut count = 0;
415 let result = ((*vtable).EnumerateDistributions)(
416 self.0.as_raw(),
417 std::ptr::from_mut(&mut count),
418 std::ptr::from_mut(&mut distros),
419 std::ptr::from_mut(&mut error_info),
420 );
421 if result.is_ok() {
422 Ok((count, distros))
423 } else {
424 Err((result, error_info))
425 }
426 }
427 }
428
429 pub unsafe fn SetVersion(
430 &self,
431 distro_guid: GUID,
432 version: u32,
433 stderr_handle: HANDLE,
434 ) -> LxssResult<()> {
435 unsafe {
436 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
437 let mut error_info = std::mem::zeroed();
438 let result = ((*vtable).SetVersion)(
439 self.0.as_raw(),
440 std::ptr::from_ref(&distro_guid),
441 version,
442 stderr_handle,
443 std::ptr::from_mut(&mut error_info),
444 );
445 if result.is_ok() {
446 Ok(())
447 } else {
448 Err((result, error_info))
449 }
450 }
451 }
452
453 pub unsafe fn RegisterDistribution(
454 &self,
455 name: PCWSTR,
456 version: u32,
457 file_handle: HANDLE,
458 stderr_handle: HANDLE,
459 target_directory: PCWSTR,
460 flags: u32,
461 vhd_size: u64, package_family_name: PCWSTR,
463 ) -> LxssResult<RegisterDistributionResult> {
464 unsafe {
465 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
466 let mut error_info = std::mem::zeroed();
467 let mut installed_name = PWSTR::null();
468 let mut guid = MaybeUninit::uninit();
469 let result = ((*vtable).RegisterDistribution)(
470 self.0.as_raw(),
471 name,
472 version,
473 file_handle,
474 stderr_handle,
475 target_directory,
476 flags,
477 vhd_size,
478 package_family_name,
479 std::ptr::from_mut(&mut installed_name),
480 std::ptr::from_mut(&mut error_info),
481 guid.as_mut_ptr(),
482 );
483 if result.is_ok() {
484 Ok(RegisterDistributionResult {
485 Guid: guid.assume_init(),
486 InstalledName: installed_name,
487 })
488 } else {
489 Err((result, error_info))
490 }
491 }
492 }
493
494 pub unsafe fn ExportDistribution(
495 &self,
496 distro_guid: GUID,
497 file_handle: HANDLE,
498 stderr_handle: HANDLE,
499 flags: u32,
500 ) -> LxssResult<()> {
501 unsafe {
502 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
503 let mut error_info = std::mem::zeroed();
504 let result = ((*vtable).ExportDistribution)(
505 self.0.as_raw(),
506 std::ptr::from_ref(&distro_guid),
507 file_handle,
508 stderr_handle,
509 flags,
510 std::ptr::from_mut(&mut error_info),
511 );
512 if result.is_ok() {
513 Ok(())
514 } else {
515 Err((result, error_info))
516 }
517 }
518 }
519
520 pub unsafe fn CreateLxProcess(
521 &self,
522 distro_guid: GUID,
523 filename: PCSTR,
524 command_line_count: u32,
525 command_line: *const PCSTR,
526 cwd: PCWSTR, nt_path: PCWSTR, nt_env: *mut u16, nt_env_len: u32, username: PCWSTR, columns: i16,
532 rows: i16,
533 console_handle: u32, std_handles: *const LXSS_STD_HANDLES,
535 flags: u32,
536 ) -> LxssResult<CreateLxProcessResult> {
537 unsafe {
538 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
539 let mut error_info = std::mem::zeroed();
540 let mut lx_process_result = CreateLxProcessResult {
541 DistributionId: GUID::default(),
542 InstanceId: GUID::default(),
543 ProcessHandle: HANDLE::default(),
544 ServerHandle: HANDLE::default(),
545 StandardIn: HANDLE::default(),
546 StandardOut: HANDLE::default(),
547 StandardErr: HANDLE::default(),
548 CommunicationChannel: HANDLE::default(),
549 InteropSocket: HANDLE::default(),
550 };
551 let result = ((*vtable).CreateLxProcess)(
552 self.0.as_raw(),
553 std::ptr::from_ref(&distro_guid),
554 filename,
555 command_line_count,
556 command_line,
557 cwd,
558 nt_path,
559 nt_env,
560 nt_env_len,
561 username,
562 columns,
563 rows,
564 console_handle,
565 std_handles,
566 flags,
567 std::ptr::from_mut(&mut lx_process_result.DistributionId),
568 std::ptr::from_mut(&mut lx_process_result.InstanceId),
569 std::ptr::from_mut(&mut lx_process_result.ProcessHandle),
570 std::ptr::from_mut(&mut lx_process_result.ServerHandle),
571 std::ptr::from_mut(&mut lx_process_result.StandardIn),
572 std::ptr::from_mut(&mut lx_process_result.StandardOut),
573 std::ptr::from_mut(&mut lx_process_result.StandardErr),
574 std::ptr::from_mut(&mut lx_process_result.CommunicationChannel),
575 std::ptr::from_mut(&mut lx_process_result.InteropSocket),
576 std::ptr::from_mut(&mut error_info),
577 );
578 if result.is_ok() {
579 Ok(lx_process_result)
580 } else {
581 Err((result, error_info))
582 }
583 }
584 }
585
586 pub unsafe fn Shutdown(&self, force: i32) -> Result<()> {
587 unsafe {
588 let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
589 let result = ((*vtable).Shutdown)(self.0.as_raw(), force);
590 if result.is_ok() {
591 Ok(())
592 } else {
593 Err(result.into())
594 }
595 }
596 }
597}
598
599pub struct RegisterDistributionResult {
600 pub Guid: GUID,
601 pub InstalledName: PWSTR,
602}
603
604#[derive(Debug)]
605pub struct CreateLxProcessResult {
606 pub DistributionId: GUID,
607 pub InstanceId: GUID,
608 pub ProcessHandle: HANDLE,
609 pub ServerHandle: HANDLE,
610 pub StandardIn: HANDLE,
611 pub StandardOut: HANDLE,
612 pub StandardErr: HANDLE,
613 pub CommunicationChannel: HANDLE,
614 pub InteropSocket: HANDLE,
615}