mirror of
https://github.com/EasyTier/EasyTier.git
synced 2025-09-26 20:51:17 +08:00
fix macos elevate (#1177)
This commit is contained in:
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -2119,9 +2119,10 @@ dependencies = [
|
||||
"dashmap",
|
||||
"dunce",
|
||||
"easytier",
|
||||
"elevated-command",
|
||||
"gethostname 1.0.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"security-framework-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
@@ -2137,6 +2138,8 @@ dependencies = [
|
||||
"thunk-rs",
|
||||
"tokio",
|
||||
"uuid",
|
||||
"winapi",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2207,20 +2210,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elevated-command"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c410eccdcc5b759704fdb6a792afe6b01ab8a062e2c003ff2567e2697a94aa"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.7",
|
||||
"libc",
|
||||
"log",
|
||||
"winapi",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "3.0.5"
|
||||
|
@@ -40,7 +40,6 @@ chrono = { version = "0.4.37", features = ["serde"] }
|
||||
|
||||
once_cell = "1.18.0"
|
||||
dashmap = "6.0"
|
||||
elevated-command = "1.1.2"
|
||||
gethostname = "1.0.2"
|
||||
|
||||
dunce = "1.0.4"
|
||||
@@ -54,6 +53,15 @@ tauri-plugin-os = "2.3.0"
|
||||
tauri-plugin-autostart = "2.5.0"
|
||||
uuid = "1.17.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows = { version = "0.52", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
||||
winapi = { version = "0.3.9", features = ["securitybaseapi", "processthreadsapi"] }
|
||||
|
||||
[target.'cfg(target_family = "unix")'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
security-framework-sys = "2.9.0"
|
||||
|
||||
[features]
|
||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||
|
97
easytier-gui/src-tauri/src/elevate/linux.rs
Normal file
97
easytier-gui/src-tauri/src/elevate/linux.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Luis Liu. All rights reserved.
|
||||
* Licensed under the MIT License. See License in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
use super::Command;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command as StdCommand, Output};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// The implementation of state check and elevated executing varies on each platform
|
||||
impl Command {
|
||||
/// Check the state the current program running
|
||||
///
|
||||
/// Return `true` if the program is running as root, otherwise false
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let is_elevated = Command::is_elevated();
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_elevated() -> bool {
|
||||
let uid = unsafe { libc::getuid() };
|
||||
if uid == 0 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Prompting the user with a graphical OS dialog for the root password,
|
||||
/// excuting the command with escalated privileges, and return the output
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let output = elevated_cmd.output().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn output(&self) -> Result<Output> {
|
||||
let pkexec = PathBuf::from_str("/bin/pkexec")?;
|
||||
let mut command = StdCommand::new(pkexec);
|
||||
let display = env::var("DISPLAY");
|
||||
let xauthority = env::var("XAUTHORITY");
|
||||
let home = env::var("HOME");
|
||||
|
||||
command.arg("--disable-internal-agent");
|
||||
if display.is_ok() || xauthority.is_ok() || home.is_ok() {
|
||||
command.arg("env");
|
||||
if let Ok(display) = display {
|
||||
command.arg(format!("DISPLAY={}", display));
|
||||
}
|
||||
if let Ok(xauthority) = xauthority {
|
||||
command.arg(format!("XAUTHORITY={}", xauthority));
|
||||
}
|
||||
if let Ok(home) = home {
|
||||
command.arg(format!("HOME={}", home));
|
||||
}
|
||||
} else {
|
||||
if self.cmd.get_envs().any(|(_, v)| v.is_some()) {
|
||||
command.arg("env");
|
||||
}
|
||||
}
|
||||
for (k, v) in self.cmd.get_envs() {
|
||||
if let Some(value) = v {
|
||||
command.arg(format!(
|
||||
"{}={}",
|
||||
k.to_str().ok_or(anyhow!("invalid key"))?,
|
||||
value.to_str().ok_or(anyhow!("invalid value"))?
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
command.arg(self.cmd.get_program());
|
||||
let args: Vec<&OsStr> = self.cmd.get_args().collect();
|
||||
if !args.is_empty() {
|
||||
command.args(args);
|
||||
}
|
||||
|
||||
let output = command.output()?;
|
||||
Ok(output)
|
||||
}
|
||||
}
|
182
easytier-gui/src-tauri/src/elevate/macos.rs
Normal file
182
easytier-gui/src-tauri/src/elevate/macos.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Luis Liu. All rights reserved.
|
||||
* Licensed under the MIT License. See License in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Thanks to https://github.com/jorangreef/sudo-prompt/blob/master/index.js
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2015 Joran Dirk Greef
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// ...
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use super::Command;
|
||||
use anyhow::Result;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{ExitStatus, Output};
|
||||
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
|
||||
use libc::{fcntl, fileno, waitpid, EINTR, F_GETOWN};
|
||||
use security_framework_sys::authorization::{
|
||||
errAuthorizationSuccess, kAuthorizationFlagDefaults, kAuthorizationFlagDestroyRights,
|
||||
AuthorizationCreate, AuthorizationExecuteWithPrivileges, AuthorizationFree, AuthorizationRef,
|
||||
};
|
||||
|
||||
const ENV_PATH: &str = "PATH";
|
||||
|
||||
fn get_exe_path<P: AsRef<Path>>(exe_name: P) -> Option<PathBuf> {
|
||||
let exe_name = exe_name.as_ref();
|
||||
if exe_name.has_root() {
|
||||
return Some(exe_name.into());
|
||||
}
|
||||
|
||||
if let Ok(abs_path) = exe_name.canonicalize() {
|
||||
if abs_path.is_file() {
|
||||
return Some(abs_path);
|
||||
}
|
||||
}
|
||||
|
||||
env::var_os(ENV_PATH).and_then(|paths| {
|
||||
env::split_paths(&paths)
|
||||
.filter_map(|dir| {
|
||||
let full_path = dir.join(exe_name);
|
||||
if full_path.is_file() {
|
||||
Some(full_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! make_cstring {
|
||||
($s:expr) => {
|
||||
match CString::new($s.as_bytes()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "null byte in string"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn gui_runas(prog: *const i8, argv: *const *const i8) -> i32 {
|
||||
let mut authref: AuthorizationRef = ptr::null_mut();
|
||||
let mut pipe: *mut libc::FILE = ptr::null_mut();
|
||||
|
||||
if AuthorizationCreate(
|
||||
ptr::null(),
|
||||
ptr::null(),
|
||||
kAuthorizationFlagDefaults,
|
||||
&mut authref,
|
||||
) != errAuthorizationSuccess
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if AuthorizationExecuteWithPrivileges(
|
||||
authref,
|
||||
prog,
|
||||
kAuthorizationFlagDefaults,
|
||||
argv as *const *mut _,
|
||||
&mut pipe,
|
||||
) != errAuthorizationSuccess
|
||||
{
|
||||
AuthorizationFree(authref, kAuthorizationFlagDestroyRights);
|
||||
return -1;
|
||||
}
|
||||
|
||||
let pid = fcntl(fileno(pipe), F_GETOWN, 0);
|
||||
let mut status = 0;
|
||||
loop {
|
||||
let r = waitpid(pid, &mut status, 0);
|
||||
if r == -1 && io::Error::last_os_error().raw_os_error() == Some(EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AuthorizationFree(authref, kAuthorizationFlagDestroyRights);
|
||||
status
|
||||
}
|
||||
|
||||
fn runas_root_gui(cmd: &Command) -> io::Result<ExitStatus> {
|
||||
let exe: OsString = match get_exe_path(&cmd.cmd.get_program()) {
|
||||
Some(exe) => exe.into(),
|
||||
None => unsafe {
|
||||
return Ok(mem::transmute(!0));
|
||||
},
|
||||
};
|
||||
let prog = make_cstring!(exe);
|
||||
let mut args = vec![];
|
||||
for arg in cmd.cmd.get_args() {
|
||||
args.push(make_cstring!(arg))
|
||||
}
|
||||
let mut argv: Vec<_> = args.iter().map(|x| x.as_ptr()).collect();
|
||||
argv.push(ptr::null());
|
||||
|
||||
unsafe { Ok(mem::transmute(gui_runas(prog.as_ptr(), argv.as_ptr()))) }
|
||||
}
|
||||
|
||||
/// The implementation of state check and elevated executing varies on each platform
|
||||
impl Command {
|
||||
/// Check the state the current program running
|
||||
///
|
||||
/// Return `true` if the program is running as root, otherwise false
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let is_elevated = Command::is_elevated();
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_elevated() -> bool {
|
||||
let uid = unsafe { libc::getuid() };
|
||||
let euid = unsafe { libc::geteuid() };
|
||||
|
||||
match (uid, euid) {
|
||||
(0, 0) => true,
|
||||
(_, 0) => true,
|
||||
(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prompting the user with a graphical OS dialog for the root password,
|
||||
/// excuting the command with escalated privileges, and return the output
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let output = elevated_cmd.output().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn output(&self) -> Result<Output> {
|
||||
let status = runas_root_gui(self)?;
|
||||
Ok(Output {
|
||||
status,
|
||||
stdout: Vec::new(),
|
||||
stderr: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
182
easytier-gui/src-tauri/src/elevate/mod.rs
Normal file
182
easytier-gui/src-tauri/src/elevate/mod.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
#![allow(dead_code)]
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Luis Liu. All rights reserved.
|
||||
* Licensed under the MIT License. See License in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
use std::convert::From;
|
||||
use std::process::Command as StdCommand;
|
||||
|
||||
/// Wrap of std::process::command and escalate privileges while executing
|
||||
pub struct Command {
|
||||
cmd: StdCommand,
|
||||
#[allow(dead_code)]
|
||||
icon: Option<Vec<u8>>,
|
||||
#[allow(dead_code)]
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
/// Command initialization shares the same logic across all the platforms
|
||||
impl Command {
|
||||
/// Constructs a new `Command` from a std::process::Command
|
||||
/// instance, it would read the following configuration from
|
||||
/// the instance while executing:
|
||||
///
|
||||
/// * The instance's path to the program
|
||||
/// * The instance's arguments
|
||||
/// * The instance's environment variables
|
||||
///
|
||||
/// So far, the new `Command` would only take the environment variables explicitly
|
||||
/// set by std::process::Command::env and std::process::Command::env,
|
||||
/// without the ones inherited from the parent process
|
||||
///
|
||||
/// And the environment variables would only be taken on Linux and MacOS,
|
||||
/// they would be ignored on Windows
|
||||
///
|
||||
/// Current working directory would be the following while executing the command:
|
||||
/// - %SystemRoot%\System32 on Windows
|
||||
/// - /root on Linux
|
||||
/// - $TMPDIR/sudo_prompt_applet/applet.app/Contents/MacOS on MacOS
|
||||
///
|
||||
/// To pass environment variables on Windows,
|
||||
/// to inherit environment variables from the parent process and
|
||||
/// to change the working directory will be supported in later versions
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
///
|
||||
/// cmd.arg("some arg");
|
||||
/// cmd.env("some key", "some value");
|
||||
///
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new(cmd: StdCommand) -> Self {
|
||||
Self {
|
||||
cmd,
|
||||
icon: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the `Take`, returning the wrapped std::process::Command
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let cmd = elevated_cmd.into_inner();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn into_inner(self) -> StdCommand {
|
||||
self.cmd
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying std::process::Command
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let cmd = elevated_cmd.get_ref();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_ref(&self) -> &StdCommand {
|
||||
&self.cmd
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying std::process::Command
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let cmd = elevated_cmd.get_mut();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_mut(&mut self) -> &mut StdCommand {
|
||||
&mut self.cmd
|
||||
}
|
||||
|
||||
/// Set the `icon` for the pop-up graphical OS dialog
|
||||
///
|
||||
/// This method is only applicable on `MacOS`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// elevated_cmd.icon(include_bytes!("path to the icon").to_vec());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn icon(&mut self, icon: Vec<u8>) -> &mut Self {
|
||||
self.icon = Some(icon);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name for the pop-up graphical OS dialog
|
||||
///
|
||||
/// This method is only applicable on `MacOS`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// elevated_cmd.name("some name".to_string());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn name(&mut self, name: String) -> &mut Self {
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StdCommand> for Command {
|
||||
/// Converts from a std::process::Command
|
||||
///
|
||||
/// It is similiar with the construct method
|
||||
fn from(cmd: StdCommand) -> Self {
|
||||
Self {
|
||||
cmd,
|
||||
icon: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
114
easytier-gui/src-tauri/src/elevate/windows.rs
Normal file
114
easytier-gui/src-tauri/src/elevate/windows.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Luis Liu. All rights reserved.
|
||||
* Licensed under the MIT License. See License in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
use super::Command;
|
||||
use anyhow::Result;
|
||||
use std::mem;
|
||||
use std::os::windows::process::ExitStatusExt;
|
||||
use std::process::{ExitStatus, Output};
|
||||
use winapi::shared::minwindef::{DWORD, LPVOID};
|
||||
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||
use winapi::um::securitybaseapi::GetTokenInformation;
|
||||
use winapi::um::winnt::{TokenElevation, HANDLE, TOKEN_ELEVATION, TOKEN_QUERY};
|
||||
use windows::core::{w, HSTRING, PCWSTR};
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
|
||||
|
||||
/// The implementation of state check and elevated executing varies on each platform
|
||||
impl Command {
|
||||
/// Check the state the current program running
|
||||
///
|
||||
/// Return `true` if the program is running as root, otherwise false
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let is_elevated = Command::is_elevated();
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_elevated() -> bool {
|
||||
// Thanks to https://stackoverflow.com/a/8196291
|
||||
unsafe {
|
||||
let mut current_token_ptr: HANDLE = mem::zeroed();
|
||||
let mut token_elevation: TOKEN_ELEVATION = mem::zeroed();
|
||||
let token_elevation_type_ptr: *mut TOKEN_ELEVATION = &mut token_elevation;
|
||||
let mut size: DWORD = 0;
|
||||
|
||||
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut current_token_ptr);
|
||||
|
||||
if result != 0 {
|
||||
let result = GetTokenInformation(
|
||||
current_token_ptr,
|
||||
TokenElevation,
|
||||
token_elevation_type_ptr as LPVOID,
|
||||
mem::size_of::<winapi::um::winnt::TOKEN_ELEVATION_TYPE>() as u32,
|
||||
&mut size,
|
||||
);
|
||||
if result != 0 {
|
||||
return token_elevation.TokenIsElevated != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Prompting the user with a graphical OS dialog for the root password,
|
||||
/// excuting the command with escalated privileges, and return the output
|
||||
///
|
||||
/// On Windows, according to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew#return-value,
|
||||
/// Output.status.code() shoudl be greater than 32 if the function succeeds,
|
||||
/// otherwise the value indicates the cause of the failure
|
||||
///
|
||||
/// On Windows, Output.stdout and Output.stderr will always be empty as of now
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use elevated_command::Command;
|
||||
/// use std::process::Command as StdCommand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut cmd = StdCommand::new("path to the application");
|
||||
/// let elevated_cmd = Command::new(cmd);
|
||||
/// let output = elevated_cmd.output().unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn output(&self) -> Result<Output> {
|
||||
let args = self
|
||||
.cmd
|
||||
.get_args()
|
||||
.map(|c| c.to_str().unwrap().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let parameters = if args.is_empty() {
|
||||
HSTRING::new()
|
||||
} else {
|
||||
let arg_str = args.join(" ");
|
||||
HSTRING::from(arg_str)
|
||||
};
|
||||
|
||||
// according to https://stackoverflow.com/a/38034535
|
||||
// the cwd always point to %SystemRoot%\System32 and cannot be changed by settting lpdirectory param
|
||||
let r = unsafe {
|
||||
ShellExecuteW(
|
||||
HWND(0),
|
||||
w!("runas"),
|
||||
&HSTRING::from(self.cmd.get_program()),
|
||||
&HSTRING::from(parameters),
|
||||
PCWSTR::null(),
|
||||
SW_HIDE,
|
||||
)
|
||||
};
|
||||
Ok(Output {
|
||||
status: ExitStatus::from_raw(r.0 as u32),
|
||||
stdout: Vec::<u8>::new(),
|
||||
stderr: Vec::<u8>::new(),
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod elevate;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use easytier::{
|
||||
@@ -128,7 +130,7 @@ fn toggle_window_visibility<R: tauri::Runtime>(app: &tauri::AppHandle<R>) {
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn check_sudo() -> bool {
|
||||
let is_elevated = elevated_command::Command::is_elevated();
|
||||
let is_elevated = elevate::Command::is_elevated();
|
||||
if !is_elevated {
|
||||
let exe_path = std::env::var("APPIMAGE")
|
||||
.ok()
|
||||
@@ -139,7 +141,7 @@ fn check_sudo() -> bool {
|
||||
if args.contains(&AUTOSTART_ARG.to_owned()) {
|
||||
stdcmd.arg(AUTOSTART_ARG);
|
||||
}
|
||||
elevated_command::Command::new(stdcmd)
|
||||
elevate::Command::new(stdcmd)
|
||||
.output()
|
||||
.expect("Failed to run elevated command");
|
||||
}
|
||||
|
Reference in New Issue
Block a user