Support powershell and custom shells in the backend

This commit is contained in:
Félix Saparelli 2021-04-11 02:08:39 +12:00
parent 2a382a9486
commit 1280a15ca2
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
2 changed files with 82 additions and 28 deletions

View File

@ -5,10 +5,24 @@ use crate::pathop::PathOp;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::PathBuf; use std::path::PathBuf;
/// Shell to use to run commands.
///
/// `Cmd` and `Powershell` are special-cased because they have different calling
/// conventions. Also `Cmd` is only available in Windows, while `Powershell` is
/// also available on unices (provided the end-user has it installed, of course).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Shell {
None,
Unix(String),
#[cfg(windows)]
Cmd,
Powershell,
}
pub fn spawn( pub fn spawn(
cmd: &[String], cmd: &[String],
updated_paths: &[PathOp], updated_paths: &[PathOp],
shell: bool, shell: Shell,
environment: bool, environment: bool,
) -> Result<Process> { ) -> Result<Process> {
self::imp::Process::new(cmd, updated_paths, shell, environment).map_err(|e| e.into()) self::imp::Process::new(cmd, updated_paths, shell, environment).map_err(|e| e.into())
@ -18,6 +32,7 @@ pub use self::imp::Process;
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
mod imp { mod imp {
use super::Shell;
use crate::pathop::PathOp; use crate::pathop::PathOp;
use crate::signal::Signal; use crate::signal::Signal;
use nix::libc::*; use nix::libc::*;
@ -45,7 +60,7 @@ mod imp {
pub fn new( pub fn new(
cmd: &[String], cmd: &[String],
updated_paths: &[PathOp], updated_paths: &[PathOp],
shell: bool, shell: Shell,
environment: bool, environment: bool,
) -> Result<Self> { ) -> Result<Self> {
use nix::unistd::*; use nix::unistd::*;
@ -53,20 +68,27 @@ mod imp {
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
// Assemble command to run. // Assemble command to run.
// This is either the first argument from cmd (if shell == false) or "sh". // This is either the first argument from cmd (if shell == None) or "sh".
// Using "sh -c" gives us features like supporting pipes and redirects, // Using "sh -c" gives us features like supporting pipes and redirects,
// but is a little less performant and can cause trouble when using custom signals // but is a little less performant and can cause trouble when using custom signals
// (e.g. --signal SIGHUP) // (e.g. --signal SIGHUP)
let mut command = if !shell { let mut command = match shell {
Shell::None => {
let (head, tail) = cmd.split_first().expect("cmd was empty"); let (head, tail) = cmd.split_first().expect("cmd was empty");
let mut command = Command::new(head); let mut c = Command::new(head);
command.args(tail); c.args(tail);
command c
} else { }
let mut command = Command::new("sh"); Shell::Powershell => {
//command.arg("-c").arg(wrap_commands(cmd).join(" ")); let mut c = Command::new("pwsh");
command.arg("-c").arg(cmd.join(" ")); c.arg("-Command").arg(cmd.join(" "));
command c
}
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
}; };
debug!("Assembled command {:?}", command); debug!("Assembled command {:?}", command);
@ -151,6 +173,7 @@ mod imp {
#[cfg(target_family = "windows")] #[cfg(target_family = "windows")]
mod imp { mod imp {
use super::Shell;
use crate::pathop::PathOp; use crate::pathop::PathOp;
use crate::signal::Signal; use crate::signal::Signal;
use std::io; use std::io;
@ -200,7 +223,7 @@ mod imp {
pub fn new( pub fn new(
cmd: &[String], cmd: &[String],
updated_paths: &[PathOp], updated_paths: &[PathOp],
shell: bool, shell: Shell,
environment: bool, environment: bool,
) -> Result<Self> { ) -> Result<Self> {
use std::convert::TryInto; use std::convert::TryInto;
@ -262,17 +285,30 @@ mod imp {
panic!("failed to set job info: {}", last_err()); panic!("failed to set job info: {}", last_err());
} }
let mut command; let mut command = match shell {
if !shell { Shell::None => {
let (first, rest) = cmd.split_first().expect("command is empty"); let (first, rest) = cmd.split_first().expect("command is empty");
command = Command::new(first); let mut c = Command::new(first);
command.args(rest); c.args(rest);
} else {
command = Command::new("cmd.exe");
command.arg("/C");
//command.arg(wrap_commands(cmd).join(" "));
command.arg(cmd.join(" "));
} }
Shell::Cmd => {
let mut c = Command::new("cmd.exe");
c.arg("/C").arg(cmd.join(" "));
c
}
Shell::Powershell => {
let mut c = Command::new("powershell.exe");
c.arg("-Command").arg(cmd.join(" "));
c
}
// Using unixy shells on windows could be supported...
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
};
command.creation_flags(CREATE_SUSPENDED); command.creation_flags(CREATE_SUSPENDED);
debug!("Assembled command {:?}", command); debug!("Assembled command {:?}", command);

View File

@ -5,7 +5,7 @@ use crate::gitignore;
use crate::ignore; use crate::ignore;
use crate::notification_filter::NotificationFilter; use crate::notification_filter::NotificationFilter;
use crate::pathop::PathOp; use crate::pathop::PathOp;
use crate::process::{self, Process}; use crate::process::{self, Shell, Process};
use crate::signal::{self, Signal}; use crate::signal::{self, Signal};
use crate::watcher::{Event, Watcher}; use crate::watcher::{Event, Watcher};
use std::{ use std::{
@ -168,13 +168,31 @@ impl ExecHandler {
clear_screen(); clear_screen();
} }
#[cfg(windows)]
fn get_shell(config: &Config) -> Shell {
if config.no_shell {
Shell::None
} else {
Shell::Cmd
}
}
#[cfg(not(windows))]
fn get_shell(config: &Config) -> Shell {
if config.no_shell {
Shell::None
} else {
Shell::Unix("sh".into())
}
}
debug!("Launching child process"); debug!("Launching child process");
let mut guard = self.child_process.write()?; let mut guard = self.child_process.write()?;
*guard = Some(process::spawn( *guard = Some(process::spawn(
&self.args.cmd, &self.args.cmd,
ops, ops,
self.args.no_shell, get_shell(&self.args),
self.args.no_environment, !self.args.no_environment,
)?); )?);
Ok(()) Ok(())