Asynchronously invoke a command and return the exit code, stdout, and stderr. # Arguments `executable` - The command to execute `args` - Optional arguments to pass to the command `input` - Optional input to pass to the command `cwd` - Optional working directory to execute the command in `env` - Optional environment variable mappings to add or update `exit_codes` - Descriptions of exit codes, eit
(executable: &str, args: Option<Vec<String>>, input: Option<&str>, cwd: Option<&Path>, env: Option<HashMap<String, String>>, exit_codes: &ExitCodesMap)
| 759 | /// Error is returned if the command fails to execute or stdin/stdout/stderr cannot be opened. |
| 760 | /// |
| 761 | async fn run_process_async(executable: &str, args: Option<Vec<String>>, input: Option<&str>, cwd: Option<&Path>, env: Option<HashMap<String, String>>, exit_codes: &ExitCodesMap) -> Result<(i32, String, String), DscError> { |
| 762 | |
| 763 | // use somewhat large initial buffer to avoid early string reallocations; |
| 764 | // the value is based on list result of largest of built-in adapters - WMI adapter ~500KB |
| 765 | const INITIAL_BUFFER_CAPACITY: usize = 1024*1024; |
| 766 | |
| 767 | let mut command = Command::new(executable); |
| 768 | if input.is_some() { |
| 769 | command.stdin(Stdio::piped()); |
| 770 | } else { |
| 771 | command.stdin(Stdio::null()); |
| 772 | } |
| 773 | command.stdout(Stdio::piped()); |
| 774 | command.stderr(Stdio::piped()); |
| 775 | if let Some(args) = args { |
| 776 | command.args(args); |
| 777 | } |
| 778 | if let Some(cwd) = cwd { |
| 779 | command.current_dir(cwd); |
| 780 | } |
| 781 | if let Some(env) = env { |
| 782 | command.envs(env); |
| 783 | } |
| 784 | if executable == "dsc" && env::var("DEBUG_DSC").is_ok() { |
| 785 | // remove this env var from child process as it will fail reading from keyboard to allow attaching |
| 786 | command.env_remove("DEBUG_DSC"); |
| 787 | } |
| 788 | |
| 789 | let mut child = match command.spawn() { |
| 790 | Ok(c) => c, |
| 791 | Err(e) => { |
| 792 | return Err(DscError::CommandOperation(e.to_string(), executable.to_string())) |
| 793 | } |
| 794 | }; |
| 795 | |
| 796 | let Some(stdout) = child.stdout.take() else { |
| 797 | return Err(DscError::CommandOperation(t!("dscresources.commandResource.processChildStdout").to_string(), executable.to_string())); |
| 798 | }; |
| 799 | let Some(stderr) = child.stderr.take() else { |
| 800 | return Err(DscError::CommandOperation(t!("dscresources.commandResource.processChildStderr").to_string(), executable.to_string())); |
| 801 | }; |
| 802 | let mut stdout_reader = BufReader::new(stdout).lines(); |
| 803 | let mut stderr_reader = BufReader::new(stderr).lines(); |
| 804 | |
| 805 | if let Some(input) = input { |
| 806 | trace!("Writing to command STDIN: {input}"); |
| 807 | let Some(mut stdin) = child.stdin.take() else { |
| 808 | return Err(DscError::CommandOperation(t!("dscresources.commandResource.processChildStdin").to_string(), executable.to_string())); |
| 809 | }; |
| 810 | if stdin.write_all(input.as_bytes()).await.is_err() { |
| 811 | return Err(DscError::CommandOperation(t!("dscresources.commandResource.processWriteStdin").to_string(), executable.to_string())); |
| 812 | } |
| 813 | drop(stdin); |
| 814 | } |
| 815 | |
| 816 | let Some(child_id) = child.id() else { |
| 817 | return Err(DscError::CommandOperation(t!("dscresources.commandResource.processChildId").to_string(), executable.to_string())); |
| 818 | }; |
no test coverage detected