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