From bd16b5e6f91c4855345c12e3588657d8e21d8469 Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 20:58:20 +0100 Subject: [PATCH 01/10] Add wrapper script for testing wizard through chroot. --- test_with_chroot.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 test_with_chroot.sh diff --git a/test_with_chroot.sh b/test_with_chroot.sh new file mode 100755 index 0000000..1ad27e5 --- /dev/null +++ b/test_with_chroot.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +# set up variables and check existence of chroot system +if [ -z "${CHROOT_PATH}" ]; then + echo "Need to set CHROOT_PATH!" + exit 1 +fi +wizard_filename="reform-setup" +cleanup_script_filename="cleanup.sh" +repo_path_wizard="./target/debug/${wizard_filename}" +repo_path_cleanup_script="./${cleanup_script_filename}" +chroot_dir_root="${CHROOT_PATH}/root" +chroot_path_wizard="./root/${wizard_filename}" +if [ ! -d "${chroot_dir_root}" ]; then + echo "Can't find ${chroot_dir_root}, suspecting no system for chrooting set up." + echo "To set it up, run something like this:" + echo "# mkdir -p ${CHROOT_PATH}" + echo "# debootstrap unstable ${CHROOT_PATH} http://deb.debian.org/debian/" + exit 1 +fi + +# ensure the current variants of the needed executables are inside the chrooted system +cp "${repo_path_wizard}" "${chroot_dir_root}/" +cp "${repo_path_cleanup_script}" "${chroot_dir_root}/" + +# add whatever packages not provided by debootstrap on default are expected +chroot "${CHROOT_PATH}" /bin/apt install -y libgtk-4-dev libadwaita-1-dev + +# xhost +/- allows the Wizard to access the X Server for display +xhost + +chroot "${CHROOT_PATH}" "${chroot_path_wizard}" +xhost - + +exit 0 -- GitLab From aef1069cec0104a0898332f7b6c37eeadcfc4de7 Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:11:37 +0100 Subject: [PATCH 02/10] Close wizard on finish. --- src/main.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7520d07..50db81d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,7 +156,7 @@ fn build_time_page(stack: &Stack) -> StackPage { return stack.add_titled(&page_box, Some("time"), "Time"); } -fn build_desktop_page(stack: &Stack) -> StackPage { +fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage { let button_next = Button::builder().label("Next").build(); let page_box = build_box(); @@ -170,20 +170,19 @@ fn build_desktop_page(stack: &Stack) -> StackPage { page_box.append(&choice_sway); page_box.append(&button_next); + button_next.connect_clicked( + clone!(@weak window => move |_| { + let _ = window.close(); + }) + ); + return stack.add_titled(&page_box, Some("desktop"), "Desktop"); } fn build_ui(app: &Application) { let header_bar = HeaderBar::default(); - // main content stack let stack = Stack::builder().build(); - let _welcome_page = build_welcome_page(&stack); - let _keyboard_page = build_keyboard_page(&stack); - let _root_pw_page = build_root_pw_page(&stack); - let _account_page = build_account_page(&stack); - let _time_page = build_time_page(&stack); - let _desktop_page = build_desktop_page(&stack); let toolbar_view = ToolbarView::builder() .content(&stack) @@ -219,4 +218,12 @@ fn build_ui(app: &Application) { window.set_titlebar(Some(&header_bar)); window.present(); + + // main content stack + let _welcome_page = build_welcome_page(&stack); + let _keyboard_page = build_keyboard_page(&stack); + let _root_pw_page = build_root_pw_page(&stack); + let _account_page = build_account_page(&stack); + let _time_page = build_time_page(&stack); + let _desktop_page = build_desktop_page(&stack, &window); } -- GitLab From c978dbdb8a041abed42cc0154ffd5272841f087d Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:20:24 +0100 Subject: [PATCH 03/10] Add cleanup script dummy and call it on wizard finish. --- cleanup.sh | 4 ++++ src/main.rs | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 cleanup.sh diff --git a/cleanup.sh b/cleanup.sh new file mode 100644 index 0000000..8e67d30 --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e + +echo "I wish I was a proper clean-up script, for now I'm just a dummy. – $(date)" >> /root/cleanup_script.log diff --git a/src/main.rs b/src/main.rs index 50db81d..9850233 100644 --- a/src/main.rs +++ b/src/main.rs @@ -172,6 +172,9 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage { button_next.connect_clicked( clone!(@weak window => move |_| { + let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script"); + println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output of timedatectl")); + println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output of timedatectl")); let _ = window.close(); }) ); -- GitLab From 85eb5d3616eb646d400cfa77efde6f579f21463d Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:29:23 +0100 Subject: [PATCH 04/10] Fix timezone selection in chroot tests, and make it actually set timezone and localtime. --- src/main.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9850233..174fb35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -130,11 +130,18 @@ fn build_time_page(stack: &Stack) -> StackPage { let button_next = Button::builder().label("Next").build(); let page_box = build_box(); - let output = String::from_utf8(Command::new("timedatectl") - .args(["list-timezones"]) + // let output = String::from_utf8(Command::new("timedatectl") + // .args(["list-timezones"]) + // .output() + // .expect("failed to execute timedatectl").stdout) + // .expect("failed to parse utf8 output of timedatectl"); + + // above wouldn't run easily within chroot, so for now let's use this + let output = String::from_utf8(Command::new("sh") + .args(["-c", "awk '/^Z/ { print $2 }; /^L/ { print $3 }' /usr/share/zoneinfo/tzdata.zi"]) .output() - .expect("failed to execute timedatectl").stdout) - .expect("failed to parse utf8 output of timedatectl"); + .expect("failed to collect timezones").stdout) + .expect("failed to parse utf8 output of awk"); let lines:Vec<&str> = output.lines().collect(); let list = StringList::new(&lines); @@ -149,6 +156,16 @@ fn build_time_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { + + // in a non-chroot environment we might want to use timedatectl instead + let tz = list.string(dropdown.selected()).unwrap(); + let mut output = Command::new("sh").args(["-c", &format!("echo '{}' > /etc/timezone", tz)]).output().expect("failed to set timezone"); + println!("set timezone: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); + println!("set timezone: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); + output = Command::new("ln").args(["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"]).output().expect("failed to set localtime"); + println!("set localtime: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); + println!("set localtime: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); + stack.set_visible_child_full("desktop", gtk::StackTransitionType::SlideLeft); }) ); @@ -172,9 +189,11 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage { button_next.connect_clicked( clone!(@weak window => move |_| { + let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script"); - println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output of timedatectl")); - println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output of timedatectl")); + println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); + println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); + let _ = window.close(); }) ); -- GitLab From 3b6518c4cc66b18cbdf945fc1cd641c1b8e62e1c Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:32:33 +0100 Subject: [PATCH 05/10] Add adduser call on username setting. --- src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.rs b/src/main.rs index 174fb35..13a60c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,6 +119,12 @@ fn build_account_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { + + let username = username_entry.text(); + let output = Command::new("adduser").args(["--disabled-password", "--comment", "", &username]).output().expect("failed to adduser"); + println!("adduser: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); + println!("adduser: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); + stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft); }) ); -- GitLab From e859051bd4d9570ba5e6f4bf881db094b8b0d6a8 Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:41:56 +0100 Subject: [PATCH 06/10] Some refactoring. --- src/main.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/main.rs b/src/main.rs index 13a60c4..94f8131 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,13 @@ const MARGIN: i32 = 48; // - https://gtk-rs.org/gtk4-rs/stable/latest/docs/gtk4/ // - https://kerkour.com/rust-cross-compilation +fn run_command(description: &str, executable: &str, params: Vec<&str>) { + println!("STEP: {}", &description); + let output = Command::new(executable).args(params).output().expect(&format!("failed to {}", description)); + println!("STDOUT: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); + println!("STDERR: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); +} + fn main() -> glib::ExitCode { let app = Application::builder().application_id(APP_ID).build(); @@ -119,12 +126,8 @@ fn build_account_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { - let username = username_entry.text(); - let output = Command::new("adduser").args(["--disabled-password", "--comment", "", &username]).output().expect("failed to adduser"); - println!("adduser: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); - println!("adduser: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); - + run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec()); stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft); }) ); @@ -162,16 +165,10 @@ fn build_time_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { - // in a non-chroot environment we might want to use timedatectl instead let tz = list.string(dropdown.selected()).unwrap(); - let mut output = Command::new("sh").args(["-c", &format!("echo '{}' > /etc/timezone", tz)]).output().expect("failed to set timezone"); - println!("set timezone: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); - println!("set timezone: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); - output = Command::new("ln").args(["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"]).output().expect("failed to set localtime"); - println!("set localtime: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); - println!("set localtime: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); - + run_command("set timezone", "sh", ["-c", &format!("echo '{}' > /etc/timezone", tz)].to_vec()); + run_command("set localtime", "ln", ["-sf", &format!("/usr/share/zoneinfo/{}", tz), "/etc/localtime"].to_vec()); stack.set_visible_child_full("desktop", gtk::StackTransitionType::SlideLeft); }) ); @@ -195,11 +192,7 @@ fn build_desktop_page(stack: &Stack, window: &ApplicationWindow) -> StackPage { button_next.connect_clicked( clone!(@weak window => move |_| { - - let output = Command::new("sh").args(["/root/cleanup.sh"]).output().expect("failed to run cleanup script"); - println!("cleanup.sh: stdout: {}", String::from_utf8(output.stdout).expect("failed to parse utf8 output")); - println!("cleanup.sh: stderr: {}", String::from_utf8(output.stderr).expect("failed to parse utf8 output")); - + run_command("cleanup.sh", "sh", ["/root/cleanup.sh"].to_vec()); let _ = window.close(); }) ); -- GitLab From a6134c83fabd66799828cfe47b88699c6461507c Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:46:36 +0100 Subject: [PATCH 07/10] Add very hacky password setting. --- src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.rs b/src/main.rs index 94f8131..0494845 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,6 +101,9 @@ fn build_root_pw_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { + let pw = pw_entry.text(); + // TODO: find less hacky approach to automatized password setting + run_command("set root pw", "sh", ["-c", &format!("echo -n 'root:{}' | chpasswd", &pw)].to_vec()); stack.set_visible_child_full("account", gtk::StackTransitionType::SlideLeft); }) ); @@ -127,7 +130,10 @@ fn build_account_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { let username = username_entry.text(); + let pw = pw_entry.text(); run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec()); + // TODO: find less hacky approach to automatized password setting + run_command("set user pw", "sh", ["-c", &format!("echo -n '{}:{}' | chpasswd", &username, &pw)].to_vec()); stack.set_visible_child_full("time", gtk::StackTransitionType::SlideLeft); }) ); -- GitLab From 709dfa6f35df10c76522ac5a318bb8879fd302d8 Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 21:58:26 +0100 Subject: [PATCH 08/10] Add most basic input validation. --- src/main.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main.rs b/src/main.rs index 0494845..6b2dfb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,14 @@ const MARGIN: i32 = 48; // - https://gtk-rs.org/gtk4-rs/stable/latest/docs/gtk4/ // - https://kerkour.com/rust-cross-compilation +fn valid_username(name: &str) -> bool { + return name.len() > 0 +} + +fn valid_password(pw: &str) -> bool { + return pw.len() > 0 +} + fn run_command(description: &str, executable: &str, params: Vec<&str>) { println!("STEP: {}", &description); let output = Command::new(executable).args(params).output().expect(&format!("failed to {}", description)); @@ -102,6 +110,10 @@ fn build_root_pw_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { let pw = pw_entry.text(); + let pw_retry = pw_entry_rep.text(); + if pw != pw_retry || !valid_password(&pw) { + return; + } // TODO: find less hacky approach to automatized password setting run_command("set root pw", "sh", ["-c", &format!("echo -n 'root:{}' | chpasswd", &pw)].to_vec()); stack.set_visible_child_full("account", gtk::StackTransitionType::SlideLeft); @@ -131,6 +143,10 @@ fn build_account_page(stack: &Stack) -> StackPage { clone!(@weak stack => move |_| { let username = username_entry.text(); let pw = pw_entry.text(); + let pw_retry = pw_entry_rep.text(); + if pw != pw_retry || !valid_username(&username) || !valid_password(&pw) { + return; + } run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec()); // TODO: find less hacky approach to automatized password setting run_command("set user pw", "sh", ["-c", &format!("echo -n '{}:{}' | chpasswd", &username, &pw)].to_vec()); -- GitLab From 7c61a2aeda9eab201d9b49df35ebfb7f68d78b8c Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 22:22:50 +0100 Subject: [PATCH 09/10] Add draft for keyboard setup, seemingly not yet working. --- src/main.rs | 6 ++++++ test_with_chroot.sh | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 6b2dfb5..138dd6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,6 +77,7 @@ fn build_keyboard_page(stack: &Stack) -> StackPage { "QWERTY-JP (Japanese)", "AZERTY-FR (French)", ]); + let keymaps = ["us", "de", "uk", "jp", "fr"]; let dropdown = DropDown::builder() .model(&list) .build(); @@ -87,6 +88,11 @@ fn build_keyboard_page(stack: &Stack) -> StackPage { button_next.connect_clicked( clone!(@weak stack => move |_| { + // FIXME: so far this does not seem to do enough to actually set up the keyboard-layout + let layout = keymaps[dropdown.selected() as usize]; + let debconf_selection = format!("keyboard-configuration keyboard-configuration/layout select {}", &layout); + run_command("set keyboard layout", "sh", ["-c", &format!("echo '{}' | debconf-set-selections", debconf_selection)].to_vec()); + run_command("run dpkg-reconfigure", "dpkg-reconfigure", ["-f", "noninteractive", "keyboard-configuration"].to_vec()); stack.set_visible_child_full("root-password", gtk::StackTransitionType::SlideLeft); }) ); diff --git a/test_with_chroot.sh b/test_with_chroot.sh index 1ad27e5..ffe2fa1 100755 --- a/test_with_chroot.sh +++ b/test_with_chroot.sh @@ -24,9 +24,12 @@ fi cp "${repo_path_wizard}" "${chroot_dir_root}/" cp "${repo_path_cleanup_script}" "${chroot_dir_root}/" -# add whatever packages not provided by debootstrap on default are expected +# packages needed for GTK4 chroot "${CHROOT_PATH}" /bin/apt install -y libgtk-4-dev libadwaita-1-dev +# packages needed for keyboard configuration (?) +chroot "${CHROOT_PATH}" /bin/apt install -y keyboard-configuration + # xhost +/- allows the Wizard to access the X Server for display xhost + chroot "${CHROOT_PATH}" "${chroot_path_wizard}" -- GitLab From 6c493ab48ca0dff9d7d7bd9a5be318f0e4451c6f Mon Sep 17 00:00:00 2001 From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 5 Mar 2024 22:30:45 +0100 Subject: [PATCH 10/10] Add display of error messages on failed input validations. --- src/main.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 138dd6a..2b9530a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,13 +111,19 @@ fn build_root_pw_page(stack: &Stack) -> StackPage { page_box.append(&pw_entry); page_box.append(&Label::new(Some("And repeat it for safety:"))); page_box.append(&pw_entry_rep); + let error_label = Label::new(None); + page_box.append(&error_label); page_box.append(&button_next); button_next.connect_clicked( clone!(@weak stack => move |_| { let pw = pw_entry.text(); let pw_retry = pw_entry_rep.text(); - if pw != pw_retry || !valid_password(&pw) { + if pw != pw_retry { + error_label.set_label("ERROR! Passwords don't match!"); + return; + } else if !valid_password(&pw) { + error_label.set_label("ERROR! Invalid password format!"); return; } // TODO: find less hacky approach to automatized password setting @@ -143,14 +149,24 @@ fn build_account_page(stack: &Stack) -> StackPage { page_box.append(&pw_entry); page_box.append(&Label::new(Some("And repeat it for safety:"))); page_box.append(&pw_entry_rep); + let error_label = Label::new(None); + page_box.append(&error_label); page_box.append(&button_next); button_next.connect_clicked( clone!(@weak stack => move |_| { let username = username_entry.text(); + if !valid_username(&username) { + error_label.set_label("ERROR! Invalid username format!"); + return; + } let pw = pw_entry.text(); let pw_retry = pw_entry_rep.text(); - if pw != pw_retry || !valid_username(&username) || !valid_password(&pw) { + if pw != pw_retry { + error_label.set_label("ERROR! Passwords don't match!"); + return; + } else if !valid_password(&pw) { + error_label.set_label("ERROR! Invalid password format!"); return; } run_command("adduser", "adduser", ["--disabled-password", "--comment", "", &username].to_vec()); -- GitLab