44
55#![ stable( feature = "process_extensions" , since = "1.2.0" ) ]
66
7- use crate :: ffi:: OsStr ;
7+ use crate :: ffi:: { OsStr , c_void} ;
8+ use crate :: mem:: MaybeUninit ;
89use crate :: os:: windows:: io:: {
910 AsHandle , AsRawHandle , BorrowedHandle , FromRawHandle , IntoRawHandle , OwnedHandle , RawHandle ,
1011} ;
1112use crate :: sealed:: Sealed ;
1213use crate :: sys_common:: { AsInner , AsInnerMut , FromInner , IntoInner } ;
13- use crate :: { process, sys} ;
14+ use crate :: { io , marker , process, ptr , sys} ;
1415
1516#[ stable( feature = "process_extensions" , since = "1.2.0" ) ]
1617impl FromRawHandle for process:: Stdio {
@@ -295,41 +296,25 @@ pub trait CommandExt: Sealed {
295296 #[ unstable( feature = "windows_process_extensions_async_pipes" , issue = "98289" ) ]
296297 fn async_pipes ( & mut self , always_async : bool ) -> & mut process:: Command ;
297298
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 .
300301 ///
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.
312306 ///
313307 /// # Note
314308 ///
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.
324311 ///
325312 /// # Example
326313 ///
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+ /// ```
331315 /// #![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};
333318 /// use std::process::Command;
334319 ///
335320 /// # struct ProcessDropGuard(std::process::Child);
@@ -338,36 +323,27 @@ pub trait CommandExt: Sealed {
338323 /// # let _ = self.0.kill();
339324 /// # }
340325 /// # }
341- ///
326+ /// #
342327 /// 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 );
345330 ///
346331 /// 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();
347336 ///
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)?;
351338 /// #
352- /// # let parent = ProcessDropGuard(parent);
353- ///
354- /// let mut child = child_cmd.spawn()?;
355- ///
356339 /// # child.kill()?;
357340 /// # Ok::<(), std::io::Error>(())
358341 /// ```
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.
365342 #[ unstable( feature = "windows_process_extensions_raw_attribute" , issue = "114854" ) ]
366- unsafe fn raw_attribute < T : Copy + Send + Sync + ' static > (
343+ fn spawn_with_attributes (
367344 & mut self ,
368- attribute : usize ,
369- value : T ,
370- ) -> & mut process:: Command ;
345+ attribute_list : & ProcThreadAttributeList < ' _ > ,
346+ ) -> io:: Result < process:: Child > ;
371347}
372348
373349#[ stable( feature = "windows_process_extensions" , since = "1.16.0" ) ]
@@ -401,13 +377,13 @@ impl CommandExt for process::Command {
401377 self
402378 }
403379
404- unsafe fn raw_attribute < T : Copy + Send + Sync + ' static > (
380+ fn spawn_with_attributes (
405381 & 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 )
411387 }
412388}
413389
@@ -447,3 +423,245 @@ impl ExitCodeExt for process::ExitCode {
447423 process:: ExitCode :: from_inner ( From :: from ( raw) )
448424 }
449425}
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