run_as/
lib.rs

1//! This library implements basic support for running a command in an elevated context.
2//!
3//! In particular this runs a command through "sudo" or other platform equivalents.
4//!
5//! ## Basic Usage
6//!
7//! The library provides a single struct called `Command` which largely follows the
8//! API of `std::process::Command`.  However it does not support capturing output or
9//! gives any guarantees for the working directory or environment.  This is because
10//! the platform APIs do not have support for that either in some cases.
11//!
12//! In particular the working directory is always the system32 folder on windows and
13//! the environment variables are always the ones of the initial system session on
14//! OS X if the GUI mode is used.
15//!
16//! ```rust,no_run
17//! use run_as::Command;
18//!
19//! let status = Command::new("rm")
20//!     .arg("/usr/local/my-app")
21//!     .status()
22//!     .unwrap();
23//! ```
24//!
25//! ## Platform Support
26//!
27//! The following platforms are supported:
28//!
29//! * Windows: always GUI mode
30//! * OS X: GUI and CLI mode
31//! * Linux: GUI and CLI mode
32
33use std::ffi::{OsStr, OsString};
34
35#[cfg(target_os = "macos")]
36mod impl_darwin;
37#[cfg(unix)]
38mod impl_unix;
39#[cfg(windows)]
40mod impl_windows;
41mod restart_self;
42
43pub use crate::restart_self::{restart_self, restart_self_elevated};
44
45#[cfg(unix)]
46pub use crate::impl_unix::is_elevated;
47
48#[cfg(windows)]
49pub use crate::impl_windows::is_elevated;
50
51#[cfg(target_os = "linux")]
52pub(crate) const PKEXEC_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
53
54/// A process builder for elevated execution
55pub struct Command {
56    command: OsString,
57    args: Vec<OsString>,
58    force_prompt: bool,
59    hide: bool,
60    gui: bool,
61    wait_to_complete: bool,
62    #[cfg(target_os = "linux")]
63    pkexec_timeout: Option<std::time::Duration>,
64}
65
66/// The `Command` type acts as a process builder for spawning programs that run in
67/// an elevated context.
68///
69/// Example:
70///
71/// ```rust,no_run
72/// use run_as::Command;
73/// let status = Command::new("cmd").status();
74/// ```
75impl Command {
76    /// Creates a new command type for a given program.
77    ///
78    /// The default configuration is to spawn without arguments, to be visible and
79    /// to not be launched from a GUI context.
80    pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
81        let command = program.as_ref().to_os_string();
82        log::debug!("Command::new {command:?}");
83        Command {
84            command,
85            args: vec![],
86            hide: false,
87            gui: false,
88            force_prompt: true,
89            wait_to_complete: true,
90            #[cfg(target_os = "linux")]
91            pkexec_timeout: Some(PKEXEC_TIMEOUT),
92        }
93    }
94
95    /// Add an argument to pass to the program.
96    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
97        self.args.push(arg.as_ref().to_os_string());
98        self
99    }
100
101    /// Add multiple arguments to pass to the program.
102    pub fn args<I, S>(&mut self, args: I) -> &mut Command
103    where
104        I: IntoIterator<Item = S>,
105        S: AsRef<OsStr>,
106    {
107        for arg in args {
108            self.arg(arg.as_ref());
109        }
110        self
111    }
112
113    /// Controls the visibility of the program on supported platforms.  The default is
114    /// to launch the program visible.
115    pub fn show(&mut self, val: bool) -> &mut Command {
116        self.hide = !val;
117        self
118    }
119
120    /// Controls the GUI context.  The default behavior is to assume that the program is
121    /// launched from a command line (not using a GUI).  This primarily controls how the
122    /// elevation prompt is rendered.  On some platforms like Windows the elevation prompt
123    /// is always a GUI element.
124    ///
125    /// If the preferred mode is not available it falls back to the other automatically.
126    pub fn gui(&mut self, val: bool) -> &mut Command {
127        self.gui = val;
128        self
129    }
130
131    /// Can disable the prompt forcing for supported platforms.  Mostly this allows sudo
132    /// on unix platforms to not prompt for a password.
133    pub fn force_prompt(&mut self, val: bool) -> &mut Command {
134        self.force_prompt = val;
135        self
136    }
137
138    /// Controls whether to wait for the command to complete.  The default is to wait.
139    /// If set to false the command is started and the function returns immediately.
140    /// The exit status in that case is always reported as success.
141    pub fn wait_to_complete(&mut self, val: bool) -> &mut Command {
142        self.wait_to_complete = val;
143        self
144    }
145
146    /// Sets the timeout for pkexec on Linux.
147    #[cfg(target_os = "linux")]
148    pub fn pkexec_timeout(&mut self, val: Option<std::time::Duration>) -> &mut Command {
149        self.pkexec_timeout = val;
150        self
151    }
152
153    /// Executes a command as a child process, waiting for it to finish and
154    /// collecting its exit status.
155    pub fn status(&mut self) -> std::io::Result<std::process::ExitStatus> {
156        #[cfg(all(unix, target_os = "macos"))]
157        use crate::impl_darwin::runas_impl;
158        #[cfg(all(unix, not(target_os = "macos")))]
159        use impl_unix::runas_impl;
160        #[cfg(windows)]
161        use impl_windows::runas_impl;
162        runas_impl(self)
163    }
164}