4
4
5
5
#![ stable( feature = "process_extensions" , since = "1.2.0" ) ]
6
6
7
- use crate :: ffi:: OsStr ;
7
+ use crate :: ffi:: { c_void, OsStr } ;
8
+ use crate :: mem:: MaybeUninit ;
8
9
use crate :: os:: windows:: io:: {
9
10
AsHandle , AsRawHandle , BorrowedHandle , FromRawHandle , IntoRawHandle , OwnedHandle , RawHandle ,
10
11
} ;
11
12
use crate :: sealed:: Sealed ;
12
13
use crate :: sys_common:: { AsInner , AsInnerMut , FromInner , IntoInner } ;
13
- use crate :: { process, sys} ;
14
+ use crate :: { io , marker , process, ptr , sys} ;
14
15
15
16
#[ stable( feature = "process_extensions" , since = "1.2.0" ) ]
16
17
impl FromRawHandle for process:: Stdio {
@@ -295,41 +296,26 @@ pub trait CommandExt: Sealed {
295
296
#[ unstable( feature = "windows_process_extensions_async_pipes" , issue = "98289" ) ]
296
297
fn async_pipes ( & mut self , always_async : bool ) -> & mut process:: Command ;
297
298
298
- /// Set a raw attribute on the command, providing extended configuration options for Windows
299
- /// processes .
299
+ /// Executes the command as a child process with the given
300
+ /// [`ProcThreadAttributeList`], returning a handle to it .
300
301
///
301
- /// This method allows you to specify custom attributes for a child process on Windows systems
302
- /// using raw attribute values. Raw attributes provide extended configurability for process
303
- /// creation, but their usage can be complex and potentially unsafe.
304
- ///
305
- /// The `attribute` parameter specifies the raw attribute to be set, while the `value`
306
- /// parameter holds the value associated with that attribute. Please refer to the
307
- /// [`windows-rs` documentation] or the [Win32 API documentation] for detailed information
308
- /// about available attributes and their meanings.
309
- ///
310
- /// [`windows-rs` documentation]: https://microsoft.github.io/windows-docs-rs/doc/windows/
311
- /// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute
302
+ /// This method enables the customization of attributes for the spawned
303
+ /// child process on Windows systems.
304
+ /// Attributes offer extended configurability for process creation,
305
+ /// but their usage can be intricate and potentially unsafe.
312
306
///
313
307
/// # Note
314
308
///
315
- /// The maximum number of raw attributes is the value of [`u32::MAX`].
316
- /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error`
317
- /// indicating that the maximum number of attributes has been exceeded.
318
- ///
319
- /// # Safety
320
- ///
321
- /// The usage of raw attributes is potentially unsafe and should be done with caution.
322
- /// Incorrect attribute values or improper configuration can lead to unexpected behavior or
323
- /// errors.
309
+ /// By default, stdin, stdout, and stderr are inherited from the parent
310
+ /// process.
324
311
///
325
312
/// # Example
326
313
///
327
- /// The following example demonstrates how to create a child process with a specific parent
328
- /// process ID using a raw attribute.
329
- ///
330
- /// ```rust
314
+ /// ```
331
315
/// #![feature(windows_process_extensions_raw_attribute)]
332
- /// use std::os::windows::{process::CommandExt, io::AsRawHandle};
316
+ /// use std::os::windows::process::ProcThreadAttributeList;
317
+ /// use std::os::windows::process::CommandExt;
318
+ /// use std::os::windows::io::AsRawHandle;
333
319
/// use std::process::Command;
334
320
///
335
321
/// # struct ProcessDropGuard(std::process::Child);
@@ -338,36 +324,27 @@ pub trait CommandExt: Sealed {
338
324
/// # let _ = self.0.kill();
339
325
/// # }
340
326
/// # }
341
- ///
327
+ /// #
342
328
/// let parent = Command::new("cmd").spawn()?;
343
- ///
344
- /// let mut child_cmd = Command::new("cmd" );
329
+ /// let parent_process_handle = parent.as_raw_handle();
330
+ /// # let parent = ProcessDropGuard(parent );
345
331
///
346
332
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
333
+ /// let mut attribute_list = ProcThreadAttributeList::build()
334
+ /// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
335
+ /// .finish()
336
+ /// .unwrap();
347
337
///
348
- /// unsafe {
349
- /// child_cmd.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.as_raw_handle() as isize);
350
- /// }
338
+ /// let mut child = Command::new("cmd").spawn_with_attributes(attribute_list)?;
351
339
/// #
352
- /// # let parent = ProcessDropGuard(parent);
353
- ///
354
- /// let mut child = child_cmd.spawn()?;
355
- ///
356
340
/// # child.kill()?;
357
341
/// # Ok::<(), std::io::Error>(())
358
342
/// ```
359
- ///
360
- /// # Safety Note
361
- ///
362
- /// Remember that improper use of raw attributes can lead to undefined behavior or security
363
- /// vulnerabilities. Always consult the documentation and ensure proper attribute values are
364
- /// used.
365
343
#[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
366
- unsafe fn raw_attribute < T : Copy + Send + Sync + ' static > (
344
+ fn spawn_with_attributes (
367
345
& mut self ,
368
- attribute : usize ,
369
- value : T ,
370
- ) -> & mut process:: Command ;
346
+ attribute_list : & ProcThreadAttributeList < ' _ > ,
347
+ ) -> io:: Result < process:: Child > ;
371
348
}
372
349
373
350
#[ stable( feature = "windows_process_extensions" , since = "1.16.0" ) ]
@@ -401,13 +378,13 @@ impl CommandExt for process::Command {
401
378
self
402
379
}
403
380
404
- unsafe fn raw_attribute < T : Copy + Send + Sync + ' static > (
381
+ fn spawn_with_attributes (
405
382
& mut self ,
406
- attribute : usize ,
407
- value : T ,
408
- ) -> & mut process :: Command {
409
- unsafe { self . as_inner_mut ( ) . raw_attribute ( attribute , value ) } ;
410
- self
383
+ attribute_list : & ProcThreadAttributeList < ' _ > ,
384
+ ) -> io :: Result < process :: Child > {
385
+ self . as_inner_mut ( )
386
+ . spawn_with_attributes ( sys :: process :: Stdio :: Inherit , true , Some ( attribute_list ) )
387
+ . map ( process :: Child :: from_inner )
411
388
}
412
389
}
413
390
@@ -447,3 +424,234 @@ impl ExitCodeExt for process::ExitCode {
447
424
process:: ExitCode :: from_inner ( From :: from ( raw) )
448
425
}
449
426
}
427
+
428
+ /// A wrapper around windows [`ProcThreadAttributeList`][1].
429
+ ///
430
+ /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist>
431
+ #[ derive( Debug ) ]
432
+ #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
433
+ pub struct ProcThreadAttributeList < ' a > {
434
+ attribute_list : Box < [ MaybeUninit < u8 > ] > ,
435
+ _lifetime_marker : marker:: PhantomData < & ' a ( ) > ,
436
+ }
437
+
438
+ #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
439
+ impl < ' a > ProcThreadAttributeList < ' a > {
440
+ /// Creates a new builder for constructing a [`ProcThreadAttributeList`].
441
+ pub fn build ( ) -> ProcThreadAttributeListBuilder < ' a > {
442
+ ProcThreadAttributeListBuilder :: new ( )
443
+ }
444
+
445
+ /// Returns a pointer to the underling attribute list.
446
+ #[ doc( hidden) ]
447
+ pub fn as_ptr ( & self ) -> * const MaybeUninit < u8 > {
448
+ self . attribute_list . as_ptr ( )
449
+ }
450
+ }
451
+
452
+ #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
453
+ impl < ' a > Drop for ProcThreadAttributeList < ' a > {
454
+ /// Deletes the attribute list.
455
+ ///
456
+ /// This method calls [`DeleteProcThreadAttributeList`][1] to delete the
457
+ /// underlying attribute list.
458
+ ///
459
+ /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-deleteprocthreadattributelist>
460
+ fn drop ( & mut self ) {
461
+ let lp_attribute_list = self . attribute_list . as_mut_ptr ( ) . cast :: < c_void > ( ) ;
462
+ unsafe { sys:: c:: DeleteProcThreadAttributeList ( lp_attribute_list) }
463
+ }
464
+ }
465
+
466
+ /// Builder for constructing a [`ProcThreadAttributeList`].
467
+ #[ derive( Clone , Debug ) ]
468
+ #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
469
+ pub struct ProcThreadAttributeListBuilder < ' a > {
470
+ attributes : alloc:: collections:: BTreeMap < usize , ProcThreadAttributeValue > ,
471
+ _lifetime_marker : marker:: PhantomData < & ' a ( ) > ,
472
+ }
473
+
474
+ #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
475
+ impl < ' a > ProcThreadAttributeListBuilder < ' a > {
476
+ fn new ( ) -> Self {
477
+ ProcThreadAttributeListBuilder {
478
+ attributes : alloc:: collections:: BTreeMap :: new ( ) ,
479
+ _lifetime_marker : marker:: PhantomData ,
480
+ }
481
+ }
482
+
483
+ /// Sets an attribute on the attribute list.
484
+ ///
485
+ /// The `attribute` parameter specifies the raw attribute to be set, while
486
+ /// the `value` parameter holds the value associated with that attribute.
487
+ /// Please refer to the [Windows documentation][1] for a list of valid attributes.
488
+ ///
489
+ /// # Note
490
+ ///
491
+ /// The maximum number of attributes is the value of [`u32::MAX`]. If this
492
+ /// limit is exceeded, the call to [`Self::finish`] will return an `Error`
493
+ /// indicating that the maximum number of attributes has been exceeded.
494
+ ///
495
+ /// # Safety Note
496
+ ///
497
+ /// Remember that improper use of attributes can lead to undefined behavior
498
+ /// or security vulnerabilities. Always consult the documentation and ensure
499
+ /// proper attribute values are used.
500
+ ///
501
+ /// [1]: <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute#parameters>
502
+ pub fn attribute < T > ( self , attribute : usize , value : & ' a T ) -> Self {
503
+ unsafe {
504
+ self . raw_attribute (
505
+ attribute,
506
+ ptr:: addr_of!( * value) . cast :: < c_void > ( ) ,
507
+ crate :: mem:: size_of :: < T > ( ) ,
508
+ )
509
+ }
510
+ }
511
+
512
+ /// Sets a raw attribute on the attribute list.
513
+ ///
514
+ /// This function is useful for setting attributes with pointers or sizes
515
+ /// that cannot be derived directly from their values.
516
+ ///
517
+ /// # Safety
518
+ ///
519
+ /// This function is marked as `unsafe` because it deals with raw pointers
520
+ /// and sizes. It is the responsibility of the caller to ensure the value
521
+ /// lives longer than the [`ProcThreadAttributeListBuilder`] as well as
522
+ /// the validity of the size parameter.
523
+ ///
524
+ /// # Example
525
+ /// ```
526
+ /// use std::ffi::c_void;
527
+ /// use std::mem::{self, zeroed};
528
+ /// use std::os::windows::process::ProcThreadAttributeList;
529
+ /// use std::os::windows::process::CommandExt;
530
+ /// use std::os::windows::raw::HANDLE;
531
+ /// use std::process::Command;
532
+ ///
533
+ /// #[repr(C)]
534
+ /// pub struct COORD {
535
+ /// pub X: i16,
536
+ /// pub Y: i16,
537
+ /// }
538
+ ///
539
+ /// extern "system" {
540
+ /// fn CreatePipe(hreadpipe: HANDLE, hwritepipe: HANDLE, lppipeattributes: *const c_void, nsize: u32) -> i32;
541
+ /// fn CreatePseudoConsole(size: COORD, hinput: HANDLE, houtput: HANDLE, dwflags: u32, phpc: *mut isize) -> i32;
542
+ /// fn CloseHandle(hobject: HANDLE) -> i32;
543
+ /// }
544
+ ///
545
+ /// let (mut input_read_side, mut output_write_side) = unsafe { (zeroed(), zeroed()) };
546
+ /// let (mut output_read_side, mut input_write_side) = unsafe { (zeroed(), zeroed()) };
547
+ ///
548
+ /// unsafe {
549
+ /// CreatePipe(&mut input_read_side, &mut input_write_side, None, 0);
550
+ /// CreatePipe(&mut output_read_side, &mut output_write_side, None, 0);
551
+ /// }
552
+ ///
553
+ /// let size = COORD { X: 60, Y: 40 };
554
+ /// let mut h_pc = unsafe { zeroed() };
555
+ /// unsafe { CreatePseudoConsole(size, input_read_side, output_write_side, 0, &mut h_pc) };
556
+ ///
557
+ /// unsafe { CloseHandle(input_read_side) };
558
+ /// unsafe { CloseHandle(output_write_side) };
559
+ ///
560
+ /// const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: u32 = 131094u32;
561
+ ///
562
+ /// let attribute_list = unsafe {
563
+ /// ProcThreadAttributeList::build()
564
+ /// .raw_attribute(
565
+ /// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
566
+ /// h_pc as *const c_void,
567
+ /// mem::size_of::<isize>()
568
+ /// )
569
+ /// .finish()
570
+ /// };
571
+ ///
572
+ /// let child = Command::new("cmd").spawn_with_attributes(attribute_list)?;
573
+ /// #
574
+ /// # child.kill()?;
575
+ /// # Ok::<(), std::io::Error>(())
576
+ /// ```
577
+ pub unsafe fn raw_attribute < T > (
578
+ mut self ,
579
+ attribute : usize ,
580
+ value_ptr : * const T ,
581
+ value_size : usize ,
582
+ ) -> Self {
583
+ self . attributes . insert (
584
+ attribute,
585
+ ProcThreadAttributeValue { ptr : value_ptr. cast :: < c_void > ( ) , size : value_size } ,
586
+ ) ;
587
+ self
588
+ }
589
+
590
+ /// Finalizes the construction of the `ProcThreadAttributeList`.
591
+ ///
592
+ /// # Errors
593
+ ///
594
+ /// Returns an error if the maximum number of attributes is exceeded
595
+ /// or if there is an I/O error during initialization.
596
+ pub fn finish ( & self ) -> io:: Result < ProcThreadAttributeList < ' a > > {
597
+ // To initialize our ProcThreadAttributeList, we need to determine
598
+ // how many bytes to allocate for it. The Windows API simplifies this
599
+ // process by allowing us to call `InitializeProcThreadAttributeList`
600
+ // with a null pointer to retrieve the required size.
601
+ let mut required_size = 0 ;
602
+ let Ok ( attribute_count) = self . attributes . len ( ) . try_into ( ) else {
603
+ return Err ( io:: const_io_error!(
604
+ io:: ErrorKind :: InvalidInput ,
605
+ "maximum number of ProcThreadAttributes exceeded" ,
606
+ ) ) ;
607
+ } ;
608
+ unsafe {
609
+ sys:: c:: InitializeProcThreadAttributeList (
610
+ ptr:: null_mut ( ) ,
611
+ attribute_count,
612
+ 0 ,
613
+ & mut required_size,
614
+ )
615
+ } ;
616
+
617
+ let mut attribute_list = vec ! [ MaybeUninit :: uninit( ) ; required_size] . into_boxed_slice ( ) ;
618
+
619
+ // Once we've allocated the necessary memory, it's safe to invoke
620
+ // `InitializeProcThreadAttributeList` to properly initialize the list.
621
+ sys:: cvt ( unsafe {
622
+ sys:: c:: InitializeProcThreadAttributeList (
623
+ attribute_list. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
624
+ attribute_count,
625
+ 0 ,
626
+ & mut required_size,
627
+ )
628
+ } ) ?;
629
+
630
+ // # Add our attributes to the buffer.
631
+ // It's theoretically possible for the attribute count to exceed a u32
632
+ // value. Therefore, we ensure that we don't add more attributes than
633
+ // the buffer was initialized for.
634
+ for ( & attribute, value) in self . attributes . iter ( ) . take ( attribute_count as usize ) {
635
+ sys:: cvt ( unsafe {
636
+ sys:: c:: UpdateProcThreadAttribute (
637
+ attribute_list. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
638
+ 0 ,
639
+ attribute,
640
+ value. ptr ,
641
+ value. size ,
642
+ ptr:: null_mut ( ) ,
643
+ ptr:: null_mut ( ) ,
644
+ )
645
+ } ) ?;
646
+ }
647
+
648
+ Ok ( ProcThreadAttributeList { attribute_list, _lifetime_marker : marker:: PhantomData } )
649
+ }
650
+ }
651
+
652
+ /// Wrapper around the value data to be used as a Process Thread Attribute.
653
+ #[ derive( Clone , Debug ) ]
654
+ struct ProcThreadAttributeValue {
655
+ ptr : * const c_void ,
656
+ size : usize ,
657
+ }
0 commit comments