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,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::process::CommandExt;
317
+ /// use std::os::windows::io::AsRawHandle;
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,229 @@ 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 [`ProcThreadAttributeListBuilder`] as well as
521
+ /// the validity of the size parameter.
522
+ ///
523
+ /// # Example
524
+ /// ```
525
+ /// use std::mem;
526
+ /// use std::os::windows::raw::HANDLE;
527
+ ///
528
+ /// #[repr(C)]
529
+ /// pub struct COORD {
530
+ /// pub X: i16,
531
+ /// pub Y: i16,
532
+ /// }
533
+ ///
534
+ /// extern "system" {
535
+ /// fn CreatePipe(hreadpipe: HANDLE, hwritepipe: HANDLE, lppipeattributes: *const c_void, nsize: u32) -> i32;
536
+ /// fn CreatePseudoConsole(size: COORD, hinput: isize, houtput: isize, dwflags: u32, phpc: HANDLE) -> i32;
537
+ /// fn CloseHandle(hobject: isize) -> i32;
538
+ /// }
539
+ ///
540
+ /// let (mut input_read_side, mut output_write_side) = unsafe { (zeroed(), zeroed()) };
541
+ /// let (mut output_read_side, mut input_write_side) = unsafe { (zeroed(), zeroed()) };
542
+ ///
543
+ /// unsafe {
544
+ /// CreatePipe(&mut input_read_side, &mut input_write_side, None, 0);
545
+ /// CreatePipe(&mut output_read_side, &mut output_write_side, None, 0);
546
+ /// }
547
+ ///
548
+ /// let size = COORD { X: 60, Y: 40 };
549
+ /// let h_pc = unsafe { CreatePseudoConsole(size, input_read_side, output_write_side, 0) };
550
+ ///
551
+ /// unsafe { CloseHandle(input_read_side) };
552
+ /// unsafe { CloseHandle(output_write_side) };
553
+ ///
554
+ /// const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: u32 = 131094u32;
555
+ ///
556
+ /// let attribute_list = unsafe {
557
+ /// ProcThreadAttributeList::build()
558
+ /// .raw_attribute(
559
+ /// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
560
+ /// h_pc.cast::<HANDLE>,
561
+ /// mem::size_of::<HANDLE>()
562
+ /// )
563
+ /// .finish()
564
+ /// };
565
+ ///
566
+ /// let child = Command::new("cmd").spawn_with_attributes(attribute_list)?;
567
+ /// #
568
+ /// # child.kill()?;
569
+ /// # Ok::<(), std::io::Error>(())
570
+ /// ```
571
+ pub unsafe fn raw_attribute < T > (
572
+ mut self ,
573
+ attribute : usize ,
574
+ value_ptr : * const T ,
575
+ value_size : usize ,
576
+ ) -> Self {
577
+ self . attributes . insert (
578
+ attribute,
579
+ ProcThreadAttributeValue { ptr : value_ptr. cast :: < c_void > ( ) , size : value_size } ,
580
+ ) ;
581
+ self
582
+ }
583
+
584
+ /// Finalizes the construction of the `ProcThreadAttributeList`.
585
+ ///
586
+ /// # Errors
587
+ ///
588
+ /// Returns an error if the maximum number of attributes is exceeded
589
+ /// or if there is an I/O error during initialization.
590
+ pub fn finish ( & self ) -> io:: Result < ProcThreadAttributeList < ' a > > {
591
+ // To initialize our ProcThreadAttributeList, we need to determine
592
+ // how many bytes to allocate for it. The Windows API simplifies this
593
+ // process by allowing us to call `InitializeProcThreadAttributeList`
594
+ // with a null pointer to retrieve the required size.
595
+ let mut required_size = 0 ;
596
+ let Ok ( attribute_count) = self . attributes . len ( ) . try_into ( ) else {
597
+ return Err ( io:: const_io_error!(
598
+ io:: ErrorKind :: InvalidInput ,
599
+ "maximum number of ProcThreadAttributes exceeded" ,
600
+ ) ) ;
601
+ } ;
602
+ unsafe {
603
+ sys:: c:: InitializeProcThreadAttributeList (
604
+ ptr:: null_mut ( ) ,
605
+ attribute_count,
606
+ 0 ,
607
+ & mut required_size,
608
+ )
609
+ } ;
610
+
611
+ let mut attribute_list = vec ! [ MaybeUninit :: uninit( ) ; required_size] . into_boxed_slice ( ) ;
612
+
613
+ // Once we've allocated the necessary memory, it's safe to invoke
614
+ // `InitializeProcThreadAttributeList` to properly initialize the list.
615
+ sys:: cvt ( unsafe {
616
+ sys:: c:: InitializeProcThreadAttributeList (
617
+ attribute_list. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
618
+ attribute_count,
619
+ 0 ,
620
+ & mut required_size,
621
+ )
622
+ } ) ?;
623
+
624
+ // # Add our attributes to the buffer.
625
+ // It's theoretically possible for the attribute count to exceed a u32
626
+ // value. Therefore, we ensure that we don't add more attributes than
627
+ // the buffer was initialized for.
628
+ for ( & attribute, value) in self . attributes . iter ( ) . take ( attribute_count as usize ) {
629
+ sys:: cvt ( unsafe {
630
+ sys:: c:: UpdateProcThreadAttribute (
631
+ attribute_list. as_mut_ptr ( ) . cast :: < c_void > ( ) ,
632
+ 0 ,
633
+ attribute,
634
+ value. ptr ,
635
+ value. size ,
636
+ ptr:: null_mut ( ) ,
637
+ ptr:: null_mut ( ) ,
638
+ )
639
+ } ) ?;
640
+ }
641
+
642
+ Ok ( ProcThreadAttributeList { attribute_list, _lifetime_marker : marker:: PhantomData } )
643
+ }
644
+ }
645
+
646
+ /// Wrapper around the value data to be used as a Process Thread Attribute.
647
+ #[ derive( Clone , Debug ) ]
648
+ struct ProcThreadAttributeValue {
649
+ ptr : * const c_void ,
650
+ size : usize ,
651
+ }
0 commit comments