Skip to content

Commit d95de51

Browse files
committed
Show dialog boxes to guide the user when run outside of a terminal
Signed-off-by: Davide Cavalca <[email protected]>
1 parent ae82fa2 commit d95de51

File tree

5 files changed

+97
-12
lines changed

5 files changed

+97
-12
lines changed

Cargo.lock

Lines changed: 33 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ log = "0.4"
1919
nix = "0.29"
2020
serde = { version = "1.0", features = ["derive"] }
2121
xdg = "2.5"
22+
zenity-dialog = { version = "0.3.6", default-features = false, features = ["info", "error"] }

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ binfmt-dispatcher parses configuration from several sources:
2323
Configs are parsed in order and later settings win. A fully commented config is [provided](docs/binfmt-dispatcher.toml.example) and should be fairly self-explanatory.
2424

2525
## Usage
26-
When run as an interpreter, binfmt-dispatcher will parse the configs, pick the best interpreter to use based on it and the binary being run, and then run it. If enabled (via the `use_muvm` config setting), binfmt-dispatcher will use [muvm](https://github.com/AsahiLinux/muvm) to execute the interpreter in a microVM if the system page-size is not 4k.
26+
When run as an interpreter, binfmt-dispatcher will parse the configs, pick the best interpreter to use based on it and the binary being run, and then run it. If enabled (via the `use_muvm` config setting), binfmt-dispatcher will use [muvm](https://github.com/AsahiLinux/muvm) to execute the interpreter in a microVM if the system page-size is not 4k. If the interpreter or any of its dependencies are missing, binfmt-dispatcher will attempt to install them by invoking the package manager.
27+
28+
If run outside of a terminal session, binfmt-dispatcher will assume it's being run as part of a desktop environment (e.g. because a user double clicked on an x86-64 binary in a file manager). If [zenity](https://help.gnome.org/users/zenity/stable/) is installed, dialog boxes will be displayed to provide feedback to the user when necessary. If it's necessary to install any missing dependencies, the package manager will be run inside a terminal emulator via [xdg-terminal-exec](https://github.com/Vladimir-csp/xdg-terminal-exec).
2729

2830
## License
2931
This project is [MIT](https://spdx.org/licenses/MIT.html) licensed. See the [LICENSE](LICENSE) file for the full text of the license.

src/main.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::config::ConfigFile;
55

66
mod util;
77
use crate::util::get_page_size;
8+
use crate::util::{error_dialog, info_dialog};
89

910
use std::env;
1011
use std::ffi::{OsStr, OsString};
@@ -17,6 +18,14 @@ use std::process::{exit, Command};
1718
#[allow(unused_imports)]
1819
use log::{debug, error, info, trace, warn};
1920

21+
fn abort(msg: &str) {
22+
error!("{}", msg);
23+
if !stdin().is_terminal() {
24+
error_dialog(msg);
25+
}
26+
exit(1)
27+
}
28+
2029
fn main() {
2130
// Parse config
2231
let settings: ConfigFile = config::parse_config().unwrap();
@@ -36,8 +45,7 @@ fn main() {
3645
let args: Vec<OsString> = env::args_os().skip(1).collect();
3746
trace!("Args:\n{:#?}", args);
3847
if args.is_empty() {
39-
error!("No arguments passed, re-run with --help to learn more.");
40-
exit(1)
48+
abort("No arguments passed, re-run with --help to learn more.");
4149
} else {
4250
match args[0].to_str().unwrap() {
4351
"--help" => {
@@ -82,6 +90,9 @@ fn main() {
8290
"Will attempt to install missing requirements for {}",
8391
interpreter_name
8492
);
93+
if !stdin().is_terminal() {
94+
info_dialog("To run this program, {} needs to be installed.");
95+
}
8596

8697
let mut dnf_command;
8798
if stdin().is_terminal() {
@@ -102,16 +113,16 @@ fn main() {
102113
Ok(mut child) => {
103114
let status = child.wait().expect("Failed to wait on dnf process");
104115
if !status.success() {
105-
error!(
116+
debug!(
106117
"Failed to install missing requirements: dnf returned {:?}",
107118
status
108119
);
109-
exit(1);
120+
abort("The installation failed. Please try again.");
110121
}
111122
}
112123
Err(e) => {
113-
error!("Failed to execute dnf: {}", e);
114-
exit(1);
124+
debug!("Failed to execute dnf: {}", e);
125+
abort("The package manager failed to start. Please try again.");
115126
}
116127
}
117128
}
@@ -123,7 +134,10 @@ fn main() {
123134
// Use muvm if the page-size is not 4k
124135
use_muvm = size != 4096;
125136
} else {
126-
error!("Failed to get page size");
137+
debug!("Failed to get page size");
138+
if !stdin().is_terminal() {
139+
error_dialog("We were unable to detect the system page size. The program might not execute correctly.");
140+
}
127141
use_muvm = false;
128142
}
129143
}
@@ -150,6 +164,5 @@ fn main() {
150164
let _ = command.exec();
151165

152166
// If exec fails, it will not return; however, we include this to handle the case.
153-
error!("Failed to execute binary");
154-
exit(1);
167+
abort("The program failed to execute.");
155168
}

src/util.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
use libc::{sysconf, _SC_PAGESIZE};
44
use std::os::raw::c_long;
55

6+
use zenity_dialog::dialog;
7+
use zenity_dialog::ZenityDialog;
8+
use zenity_dialog::ZenityOutput;
9+
10+
use log::warn;
11+
612
pub fn get_page_size() -> Option<usize> {
713
unsafe {
814
let page_size: c_long = sysconf(_SC_PAGESIZE);
@@ -13,3 +19,35 @@ pub fn get_page_size() -> Option<usize> {
1319
}
1420
}
1521
}
22+
23+
pub fn info_dialog(msg: &str) -> bool {
24+
let result = ZenityDialog::new(dialog::Info::new().with_text(msg))
25+
.with_title(env!("CARGO_PKG_NAME"))
26+
.show();
27+
28+
if result.is_err() {
29+
warn!("Failed to create dialog, is zenity installed?");
30+
return false;
31+
}
32+
33+
match result.unwrap() {
34+
ZenityOutput::Affirmed { .. } => return true,
35+
_ => return false,
36+
}
37+
}
38+
39+
pub fn error_dialog(msg: &str) -> bool {
40+
let result = ZenityDialog::new(dialog::Error::new().with_text(msg))
41+
.with_title(env!("CARGO_PKG_NAME"))
42+
.show();
43+
44+
if result.is_err() {
45+
warn!("Failed to create dialog, is zenity installed?");
46+
return false;
47+
}
48+
49+
match result.unwrap() {
50+
ZenityOutput::Affirmed { .. } => return true,
51+
_ => return false,
52+
}
53+
}

0 commit comments

Comments
 (0)