diff --git a/map.json b/map.json index 322abdba..d4c26031 100644 --- a/map.json +++ b/map.json @@ -34,10 +34,15 @@ "path" : "src/windows/win-ignoreAllFailures.ps1", "description" : "Ignore errors if there is a failed boot, failed shutdown, or failed checkpoint. The computer will attempt to boot normally after an error occurs." }, - { - "id" : "linux-alar-fki", - "path" : "src/linux/linux-alar-fki.sh", - "description" : "alar-fki allows to recover a failed VM. Three recover scenarios are possible: fstab, initrd and kernel. NOTE: use option --run-on-repair. Se the docu for more details: https://github.com/Azure/repair-script-library/blob/master/src/linux/common/helpers/alar/README.md" + { + "id" : "linux-alar-fki", + "path" : "src/linux/linux-alar-fki.sh", + "description" : "DEPRECATED: It is recommended to use linux-alar2 instead. alar-fki allows to recover a failed VM. Three recover scenarios are possible: fstab, initrd and kernel. NOTE: use option --run-on-repair. See the docu for more details: https://github.com/Azure/repair-script-library/blob/master/src/linux/common/helpers/alar/README.md" + }, + { + "id" : "linux-alar2", + "path" : "src/linux/linux-alar2.sh", + "description" : "alar2 allows to recover a failed VM. Various actions are available like: fstab, initrd and kernel. NOTE: use option --run-on-repair. See the docu for more details: https://github.com/Azure/repair-script-library/blob/master/src/linux/common/helpers/alar2/README.md. Please be aware that alar2 needs to be build on the recovery VM first. This takes about 2min. Please be patient." }, { "id" : "win-enable-nested-hyperv", diff --git a/src/linux/common/helpers/alar2/Cargo.lock b/src/linux/common/helpers/alar2/Cargo.lock new file mode 100644 index 00000000..e2675b49 --- /dev/null +++ b/src/linux/common/helpers/alar2/Cargo.lock @@ -0,0 +1,425 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "alar2" +version = "0.9.0" +dependencies = [ + "chrono", + "clap", + "cmd_lib", + "copy_dir", + "fs_extra", + "sys-mount", + "uapi", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi 0.3.9", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cmd_lib" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42443b644d30f401746fb4483fba96b13076e1ef6cb5bca7d6a14d1d8f9f2bd9" +dependencies = [ + "cmd_lib_core", + "cmd_lib_macros", +] + +[[package]] +name = "cmd_lib_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50c50a1536e882455aeaff22015146ea143b9106fc8e116669dd078ec7b7fc8" + +[[package]] +name = "cmd_lib_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d202b0cfc28d8928ba89138c0a8248cf921b9870c6c9d60c9951092df5b62b2" +dependencies = [ + "cmd_lib_core", + "proc-macro2", + "quote", +] + +[[package]] +name = "copy_dir" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4281031634644843bd2f5aa9c48cf98fc48d6b083bd90bb11becf10deaf8b0" +dependencies = [ + "walkdir", +] + +[[package]] +name = "errno" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "loopdev" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9e35cfb6646d67059f2ca8913a90e6c60633053c103df423975297f33d6fcc" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "sys-mount" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f5703caf67c45ad3450104001b4620a605e9def0cef13dde3c9add23f73cee" +dependencies = [ + "bitflags", + "libc", + "loopdev", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi 0.3.9", +] + +[[package]] +name = "uapi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63da6a15df1fe51c36d8de9813932bfc67a2a9d12fc48ff474c6129623c8d504" +dependencies = [ + "cc", + "cfg-if", + "libc", + "uapi-proc", +] + +[[package]] +name = "uapi-proc" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abed350c54016be348226f23db01ad43b1998fee70aca4fd10b7c79c731653" +dependencies = [ + "lazy_static", + "libc", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "walkdir" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" +dependencies = [ + "kernel32-sys", + "winapi 0.2.8", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/src/linux/common/helpers/alar2/Cargo.toml b/src/linux/common/helpers/alar2/Cargo.toml new file mode 100644 index 00000000..e8c0dd3e --- /dev/null +++ b/src/linux/common/helpers/alar2/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "alar2" +version = "0.9.0" +authors = ["malachma "] +edition = "2018" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = "0.4" +sys-mount = "1.2.1" +cmd_lib = "0.8.5" +fs_extra = "1.2.0" +clap = "2.33.3" +copy_dir = "0.1.2" +uapi = "0.2.1" \ No newline at end of file diff --git a/src/linux/common/helpers/alar2/src/action.rs b/src/linux/common/helpers/alar2/src/action.rs new file mode 100644 index 00000000..a3b96618 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/action.rs @@ -0,0 +1,78 @@ + +use crate::{constants, distro, helper}; +use distro::DistroKind; +use std::{env, fs, io, process}; +use std::io::Write; +use uapi; + +pub(crate) fn run_repair_script(distro: &distro::Distro, action_name: &str) -> io::Result<()> { + helper::log_info("----- Start action -----"); + + // At first make the script executable + uapi::chmod(format!("{}/{}-impl.sh", constants::ACTION_IMPL_DIR, action_name), uapi::c::S_IXUSR | uapi::c::S_IRUSR)?; + + //if let Err(e) = cmd_lib::run_fun!(chmod 700 /tmp/action_implementation/${action_name}-impl.sh) { + // helper::log_error(format!("Setting the execute permission bit failed! {}",e).as_str()); + //} + + match env::set_current_dir(constants::RESCUE_ROOT) { + Ok(_) => {} + Err(e) => println!("Error in set current dir : {}", e), + } + + // Set the environment correct + + match distro.kind { + DistroKind::Debian | DistroKind::Ubuntu => { + env::set_var("isUbuntu", "true"); + env::remove_var("isSuse"); + env::remove_var("isRedHat"); + env::remove_var("isRedHat6"); + + } + DistroKind::Suse => { + env::set_var("isSuse", "true"); + env::remove_var("isUbuntu"); + env::remove_var("isRedHat"); + env::remove_var("isRedHat6"); + } + DistroKind::RedHatCentOS => { + env::set_var("isRedHat", "true"); + env::remove_var("isUbuntu"); + env::remove_var("isSuse"); + env::remove_var("isRedHat6"); + } + DistroKind::RedHatCentOS6 => { + env::set_var("isRedHat", "true"); + env::set_var("isRedHat6", "true"); + env::remove_var("isUbuntu"); + env::remove_var("isSuse"); + } + DistroKind::Undefined => {} // Nothing to do + } + // Execute the action script + + let output = process::Command::new("chroot") + .arg(constants::RESCUE_ROOT) + .arg("/bin/bash") + .arg("-c") + .arg(format!("{}/{}-impl.sh",constants::ACTION_IMPL_DIR, action_name)) + .output()?; + + io::stdout().write_all(&output.stdout).unwrap(); + helper::log_info("----- Action stopped -----"); + + Ok(()) +} + +pub(crate) fn is_action_available(action_name: &str) -> io::Result { + let dircontent = fs::read_dir(constants::ACTION_IMPL_DIR)?; + let mut actions = Vec::new(); + for item in dircontent { + if let Ok(entry) = item { + // We need to strip off the leading path details + actions.push(entry.path().file_name().unwrap().to_str().unwrap().to_string()); + } + } + Ok(actions.contains( &format!("{}-impl.sh",action_name) )) +} diff --git a/src/linux/common/helpers/alar2/src/action_implementation/fstab-impl.sh b/src/linux/common/helpers/alar2/src/action_implementation/fstab-impl.sh new file mode 100644 index 00000000..acab7c3e --- /dev/null +++ b/src/linux/common/helpers/alar2/src/action_implementation/fstab-impl.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +mv -f /etc/fstab{,.copy} + +# For Debian we need to instal gawk first. It comes only with mawk +if [[ -f /usr/bin/apt ]]; then + apt-get install -qq -y gawk +fi + +awk '/[[:space:]]+\/[[:space:]]+/ {print}' /etc/fstab.copy >>/etc/fstab +awk '/[[:space:]]+\/boot[[:space:]]+/ {print}' /etc/fstab.copy >>/etc/fstab +# For Suse +awk '/[[:space:]]+\/boot\/efi[[:space:]]+/ {print}' /etc/fstab.copy >>/etc/fstab +# In case we have a LVM system +awk '/rootvg-homelv/ {print}' /etc/fstab.copy >>/etc/fstab +awk '/rootvg-optlv/ {print}' /etc/fstab.copy >>/etc/fstab +awk '/rootvg-tmplv/ {print}' /etc/fstab.copy >>/etc/fstab +awk '/rootvg-usrlv/ {print}' /etc/fstab.copy >>/etc/fstab +awk '/rootvg-varlv/ {print}' /etc/fstab.copy >>/etc/fstab + +echo "Content of fstab after running the script -->" +cat /etc/fstab + +exit 0 \ No newline at end of file diff --git a/src/linux/common/helpers/alar2/src/action_implementation/grub.awk b/src/linux/common/helpers/alar2/src/action_implementation/grub.awk new file mode 100644 index 00000000..e693c948 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/action_implementation/grub.awk @@ -0,0 +1,75 @@ +#!/bin/awk + + +# +# this file is needed for RedHat 6.x distros. The grub.conf file is not regenerated by any tool +# compared to grub2. this is solved with the help of AWK and SED +# All modifications are only related to initrd. As this line is missing quite often after a kernel update +# + + +BEGIN { + i=0 + kernel_version="" +} +$0 ~ /kernel|initrd/ && $0 !~ /^#|.*#/{ + grub_directive[i]=$1 + directive_value[i]=$2 + indexLine[i]=NR + i++ + } + + +# +# Build a substring delimited by '-' but start at the second character +# +# +function substr_c2(_i) { + return substr(directive_value[_i],index(directive_value[_i],"-")+1) +} + + +# +# Find out whether the grub directive follow the right order or whether we have one missing (initrd) +# In that case we need to know the number of the lines in order to insert an initrd line +# +function directiveMissing() { + k=0 + for (j=0; j /boot/grub2/grub-cfg.patch < https://bugzilla.redhat.com/show_bug.cgi?id=1850193 + if [[ -L /boot/grub2/grubenv ]]; then + yum install -y patch + patch /boot/grub2/grub.cfg /boot/grub2/grub-cfg.patch + fi + + # These lines are required as we have the ld.so.cache not build correct + # Otherwise this can lead in no functional network afterwards + # TODO find a better solution and the root cause for it + mv /sbin/dhclient /sbin/dhclient.org +cat > /sbin/dhclient <> /etc/sysctl.conf +fi + +if [[ $isUbuntu == "true" ]]; then + set_grub_default + sed -i -e 's/GRUB_DEFAULT=.*/GRUB_DEFAULT="1>2"/' /etc/default/grub + update-grub +fi + +if [[ $isSuse == "true" ]]; then + set_grub_default + sed -i -e 's/GRUB_DEFAULT=.*/GRUB_DEFAULT="1>2"/' /etc/default/grub + grub2-mkconfig -o /boot/grub2/grub.cfg +fi + +# For reference --> https://www.linuxsecrets.com/2815-grub2-submenu-change-boot-order + +exit 0 \ No newline at end of file diff --git a/src/linux/common/helpers/alar2/src/action_implementation/test-impl.sh b/src/linux/common/helpers/alar2/src/action_implementation/test-impl.sh new file mode 100644 index 00000000..00668b13 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/action_implementation/test-impl.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# This is just a simple demo in order to print out the environment seen by the script +# The calling process is preparing the environment accordingly +printenv +pwd \ No newline at end of file diff --git a/src/linux/common/helpers/alar2/src/ade.rs b/src/linux/common/helpers/alar2/src/ade.rs new file mode 100644 index 00000000..fb46d3ed --- /dev/null +++ b/src/linux/common/helpers/alar2/src/ade.rs @@ -0,0 +1,289 @@ +use crate::constants; +use crate::distro; +use crate::helper; +use crate::mount; +use crate::redhat; +use crate::ubuntu; + +/* + +At first we need to find out whether we have to work on an encrypted OS +We can do this with lsblk in order to find out whether we have a device with the name osencrypt available + + lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 48M 0 disk +└─sda1 8:1 0 46M 0 part /mnt/azure_bek_disk +sdb 8:16 0 30G 0 disk +├─sdb1 8:17 0 29.9G 0 part / +├─sdb14 8:30 0 4M 0 part +└─sdb15 8:31 0 106M 0 part /boot/efi +sdc 8:32 0 64G 0 disk +├─sdc1 8:33 0 500M 0 part /tmp/dev/sdc1 +├─sdc2 8:34 0 500M 0 part /investigateroot/boot +├─sdc3 8:35 0 2M 0 part +└─sdc4 8:36 0 63G 0 part + └─osencrypt 253:0 0 63G 0 crypt + ├─rootvg-tmplv 253:1 0 2G 0 lvm /investigateroot/tmp + ├─rootvg-usrlv 253:2 0 10G 0 lvm /investigateroot/usr + ├─rootvg-optlv 253:3 0 2G 0 lvm /investigateroot/opt + ├─rootvg-homelv 253:4 0 1G 0 lvm /investigateroot/home + ├─rootvg-varlv 253:5 0 8G 0 lvm /investigateroot/var + └─rootvg-rootlv 253:6 0 2G 0 lvm /investigateroot +sdd 8:48 0 50G 0 disk +└─sdd1 8:49 0 50G 0 part /mnt +sr0 11:0 1 628K 0 rom + +In the next step it is required to unmount all of LVM LVs. +This is due to the fact that we need to do a fs-check on all of the partitions. This we have to do for the boot +and EFI partition as well. + +In the next step we mount them again on the usual paths for ALAR + +------ + +On a non LVM system we need to do the similar steps +On an Ubuntu 16.x distro we have these details + + lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +sda 8:0 0 50G 0 disk +└─sda1 8:1 0 50G 0 part /mnt +sdb 8:16 0 48M 0 disk +└─sdb1 8:17 0 46M 0 part /mnt/azure_bek_disk +sdc 8:32 0 30G 0 disk +├─sdc1 8:33 0 29.9G 0 part / +├─sdc14 8:46 0 4M 0 part +└─sdc15 8:47 0 106M 0 part /boot/efi +sdd 8:48 0 30G 0 disk +├─sdd1 8:49 0 29.7G 0 part +│ └─osencrypt 253:0 0 29.7G 0 crypt /investigateroot +├─sdd2 8:50 0 256M 0 part /investigateroot/boot +├─sdd14 8:62 0 4M 0 part +└─sdd15 8:63 0 106M 0 part /tmp/dev/sdd15 +sr0 11:0 1 628K 0 rom + +Ubuntu 16 or 18 don't have a seperate boot partition +If ADE is used on them an extra partition is created to store the boot and luks part on an not encrypted +partition + +*/ + +pub(crate) fn is_ade_enabled() -> bool { + cmd_lib::run_cmd!(lsblk | grep -q osencrypt).is_ok() +} + +//pub(crate) fn do_ubuntu_ade(partition_info: &Vec, mut distro: &mut distro::Distro) { +pub(crate) fn do_ubuntu_ade(partition_info: &[String], mut distro: &mut distro::Distro) { + helper::log_info("This is a recent Ubuntu 16.x/18.x with ADE enabled"); + + // Only get the EFI partition + // The root part we set manually later as we have a crypt filesystem on the usual root partition + for partition in partition_info { + if partition.contains("boot") { + helper::set_efi_part_number_and_fs(&mut distro, partition); + } + } + helper::set_efi_part_path(&mut distro); + + // Set the root_part_path manually + distro.rescue_root.root_part_path = constants::OSENCRYPT_PATH.to_string(); + + set_root_part_fs(&mut distro); + + // Due to the changed partition layout on an ADE enabled OS we have to set the boot partiton details + // We use hardcoded values in this case + distro.boot_part.boot_part_fs = "ext2".to_string(); + distro.boot_part.boot_part_number = 2; + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + // Due to the fact that we have already mounted filesystems for ADE on a repair-vm + // we need to unmount them first before we can do a fsck on each of the partitions + umount_investigations(distro); + fsck_partitions(distro); + + ubuntu::verify_ubuntu(distro); +} + +//pub(crate) fn do_redhat_nolvm_ade(partition_info: &Vec, mut distro: &mut distro::Distro) { +pub(crate) fn do_redhat_nolvm_ade(partition_info: &[String], mut distro: &mut distro::Distro) { + // Unfortunately we need to work with hardcoded values as there exist no label information + distro.boot_part.boot_part_fs = "xfs".to_string(); + distro.boot_part.boot_part_number = 1; + + set_root_part_fs(&mut distro); + distro.rescue_root.root_part_number = 2; + + // Set the root_part_path manually for ADE + distro.rescue_root.root_part_path = constants::OSENCRYPT_PATH.to_string(); + + //For EFI partition we use normal logic in order to setup the details correct + for partition in partition_info.iter() { + if partition.contains("EFI") { + helper::set_efi_part_number_and_fs(&mut distro, &partition); + } + } + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::set_efi_part_path(&mut distro); + + //Unmount the investigation path, otherwise the fsck isn't possible + umount_investigations(distro); + fsck_partitions(distro); + + redhat::verify_redhat_nolvm(distro); +} + +//pub(crate) fn do_redhat6_or_7_ade(partition_info: &Vec, mut distro: &mut distro::Distro) { +pub(crate) fn do_redhat6_or_7_ade(mut distro: &mut distro::Distro) { +// Unfortunately we need to work with hardcoded values as there exist no label information + distro.boot_part.boot_part_fs = "xfs".to_string(); + distro.boot_part.boot_part_number = 1; + + set_root_part_fs(&mut distro); + distro.rescue_root.root_part_number = 2; + + // Set the root_part_path manually for ADE + distro.rescue_root.root_part_path = constants::OSENCRYPT_PATH.to_string(); + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + //Unmount the investigation path, otherwise the fsck isn't possible + umount_investigations(distro); + fsck_partitions(distro); + + redhat::verify_redhat_nolvm(distro); +} + +pub(crate) fn do_redhat_lvm_ade(partition_info: &[String], mut distro: &mut distro::Distro) { + /* + Unfortunately we need to work with hardcoded values as there exist no label information + + Number Start End Size File system Name Flags + 1 1049kB 525MB 524MB fat16 EFI System Partition boot, esp + 2 525MB 1050MB 524MB xfs msftdata + 3 1050MB 1052MB 2097kB bios_grub + 4 1052MB 68.7GB 67.7GB lvm + */ + + distro.boot_part.boot_part_fs = "xfs".to_string(); + distro.boot_part.boot_part_number = 2; + + set_root_part_fs(&mut distro); + distro.rescue_root.root_part_number = 4; + + // Set the root_part_path manually for ADE + distro.rescue_root.root_part_path = constants::OSENCRYPT_PATH.to_string(); + + //For EFI partition we use normal logic in order to setup the details correct + for partition in partition_info.iter() { + if partition.contains("EFI") { + helper::set_efi_part_number_and_fs(&mut distro, &partition); + } + } + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::set_efi_part_path(&mut distro); + + // LVM mounts need to be removed. We need to remount them later + umount_investigations_lvm(); + //Unmount the investigation path, otherwise the fsck isn't possible + umount_investigations(distro); + fsck_partitions(distro); + + // Set the LVM path details in order to work with ADE + distro.lvm_details.lvm_root_part = redhat::lvm_path_helper("rootlv"); + distro.lvm_details.lvm_usr_part = redhat::lvm_path_helper("usrlv"); + distro.lvm_details.lvm_var_part = redhat::lvm_path_helper("varlv"); + + + redhat::verify_redhat_lvm(distro); +} + +fn fsck_partitions(distro: &distro::Distro) { + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + + helper::fsck_partition( + helper::get_efi_part_path(&distro).as_str(), + helper::get_efi_part_fs(&distro).as_str(), + ); +} + +fn umount_investigations(distro: &distro::Distro) { + // umount EFI + mount::umount(helper::get_ade_mounpoint(helper::get_efi_part_path(distro).as_str()).as_str()); + + // umount boot + mount::umount(helper::get_ade_mounpoint(distro.boot_part.boot_part_path.as_str()).as_str()); + + // umount osencrypt + if !distro.is_lvm { + // If it is LVM we have already unmounted the '/investigationroot' + mount::umount(helper::get_ade_mounpoint(distro.rescue_root.root_part_path.as_str()).as_str()); + } + +} + +fn umount_investigations_lvm() { + /* + These are the mounts we have to remove + └─sdc4 8:36 0 63G 0 part + └─osencrypt 253:0 0 63G 0 crypt + ├─rootvg-tmplv 253:1 0 2G 0 lvm /investigateroot/tmp + ├─rootvg-usrlv 253:2 0 10G 0 lvm /investigateroot/usr + ├─rootvg-optlv 253:3 0 2G 0 lvm /investigateroot/opt + ├─rootvg-homelv 253:4 0 1G 0 lvm /investigateroot/home + ├─rootvg-varlv 253:5 0 8G 0 lvm /investigateroot/var + └─rootvg-rootlv 253:6 0 2G 0 lvm /investigateroot + */ + mount::umount("/investigateroot/tmp"); + mount::umount("/investigateroot/usr"); + mount::umount("/investigateroot/opt"); + mount::umount("/investigateroot/home"); + mount::umount("/investigateroot/var"); + mount::umount("/investigateroot"); +} + +fn set_root_part_fs(mut distro: &mut distro::Distro) { + if let Ok(line) = cmd_lib::run_fun!(lsblk -fn /dev/mapper/osencrypt) { + distro.rescue_root.root_part_fs = helper::cut(line.as_str(), " ", 1).to_string(); + } +} diff --git a/src/linux/common/helpers/alar2/src/cli.rs b/src/linux/common/helpers/alar2/src/cli.rs new file mode 100644 index 00000000..0be2d052 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/cli.rs @@ -0,0 +1,75 @@ +/* + +The following options and flags need to be available + +FLAG +----- +-s --standalone : This should signal that we run in standalone mode. Any required repair scripts need to be downloaded from git + +ARGUMENT +-------- +Either pass over a single action or many seperated by a comma. +Each action needs then to be verified for its existens on git/filesystem +If the action does exists it gets executed + +OPTIONS +-------- + -d --dir : The directory in which action-implementations are stored. Can be used for testing of scripts as well. + The standalone flag is necessary to be set as well + + +*/ +use clap::{App, Arg}; + +pub(crate) struct CliInfo { + pub(crate) standalone: bool, + pub(crate) action_directory: String, + pub(crate) actions: String, +} + +impl CliInfo { + pub(crate) fn new() -> Self { + Self { standalone : false, action_directory : "".to_string(), actions : "".to_string(),} + } +} + +pub(crate) fn cli() -> CliInfo { + let about = " +ALAR tries to assist with non boot able scenarios by running +one or more different actions in order to get a VM in a running state that allows +the administrator to further recover the VM after it is up, running and accessible again. +"; + let matches = App::new("Azure Linux Auto Recover") + .version("0.9") + .author("Marcus Lachmanez , malachma@microsoft.com") + .about(about) + .arg(Arg::with_name("standalone") + .short("s") + .long("standalone") + .help("Operates the tool in a standalone mode.") + .takes_value(false)) + .arg(Arg::with_name("directory") + .short("d") + .long("directory") + .takes_value(true) + .requires("standalone") // if directory is set + // it is mandatory to have standalone set as well + .help("The directory in which the actions are defined.\nRequires the standalone flag") + ) + .arg(Arg::with_name("ACTION") + .help("Sets the input file to use") + .required(true) + .index(1)) + .get_matches(); + let mut cli_info = CliInfo::new(); + + // Calling .unwrap() is safe here because "ACTION" is required + // this is true for directory as well if flag standalone is present + cli_info.actions = matches.value_of("ACTION").unwrap().to_string(); + cli_info.standalone = matches.is_present("standalone"); + if cli_info.standalone && matches.is_present("directory") { + cli_info.action_directory = matches.value_of("directory").unwrap().to_string(); + } + + cli_info +} diff --git a/src/linux/common/helpers/alar2/src/constants.rs b/src/linux/common/helpers/alar2/src/constants.rs new file mode 100644 index 00000000..a251363a --- /dev/null +++ b/src/linux/common/helpers/alar2/src/constants.rs @@ -0,0 +1,15 @@ +pub(crate) const RESCUE_DISK: &str = r#"/dev/disk/azure/scsi1/lun0"#; +pub(crate) const RESCUE_ROOT: &str = r#"/mnt/rescue-root/"#; +pub(crate) const RESCUE_ROOT_RUN: &str = r#"/mnt/rescue-root/run"#; +pub(crate) const RESCUE_ROOT_BOOT: &str = r#"/mnt/rescue-root/boot"#; +pub(crate) const RESCUE_ROOT_BOOT_EFI: &str = r#"/mnt/rescue-root/boot/efi"#; +pub(crate) const RESCUE_ROOT_USR: &str = r#"/mnt/rescue-root/usr"#; +pub(crate) const RESCUE_ROOT_VAR: &str = r#"/mnt/rescue-root/var"#; +pub(crate) const SUPPORT_FILESYSTEMS: &str = r#"dev proc sys tmp dev/pts"#; +pub(crate) const LUN_PART_PATH: &str = r#"/dev/disk/azure/scsi1/lun0-part"#; +pub(crate) const ASSERT_PATH: &str = r#"/tmp/assert"#; +pub(crate) const REDHAT_RELEASE: &str = "/tmp/assert/etc/redhat-release"; +pub(crate) const OS_RELEASE: &str = r#"/tmp/assert/etc/os-release"#; +pub(crate) const PARTITION_TMP: &str = r#"/tmp/partition_file.tmp"#; +pub(crate) const OSENCRYPT_PATH: &str = r#"/dev/mapper/osencrypt"#; +pub(crate) const ACTION_IMPL_DIR: &str = r#"/tmp/action_implementation"#; diff --git a/src/linux/common/helpers/alar2/src/distro.rs b/src/linux/common/helpers/alar2/src/distro.rs new file mode 100644 index 00000000..ad60bf3f --- /dev/null +++ b/src/linux/common/helpers/alar2/src/distro.rs @@ -0,0 +1,294 @@ +use crate::ade; +use crate::constants; +use crate::helper; +use crate::helper::read_link; +use crate::mount; +use crate::redhat; +use crate::suse; +use crate::ubuntu; +use cmd_lib; +use std::process; + +#[derive(Debug)] +pub struct Distro { + pub boot_part: BootPartDetails, + pub rescue_root: RootPartDetails, + pub efi_part: EfiPartT, + pub is_lvm: bool, + pub is_ade: bool, + pub lvm_details: LVMDetails, + pub kind: DistroKind, +} + +#[derive(Debug, PartialEq)] +pub enum DistroKind { + Debian, + Suse, + RedHatCentOS, + RedHatCentOS6, + Ubuntu, + Undefined, +} +#[derive(Debug)] +pub struct BootPartDetails { + pub(crate) boot_part_fs: String, + pub(crate) boot_part_number: u8, + pub(crate) boot_part_path: String, +} + +#[derive(Debug)] +pub struct RootPartDetails { + pub(crate) root_part_fs: String, + pub(crate) root_part_number: u8, + pub(crate) root_part_path: String, +} + +#[derive(Debug)] +pub struct LVMDetails { + pub(crate) lvm_root_part: String, + pub(crate) lvm_usr_part: String, + pub(crate) lvm_var_part: String, +} + +#[derive(Debug, PartialEq)] +pub struct EfiPartition { + pub(crate) efi_part_number: u8, + pub(crate) efi_part_fs: String, + pub(crate) efi_part_path: String, +} + +impl EfiPartition { + fn new() -> Self { + Self { + efi_part_fs: "".to_string(), + efi_part_path: "".to_string(), + efi_part_number: 0, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum EfiPartT { + EfiPart(EfiPartition), + NoEFI, +} + +impl EfiPartT { + pub fn new() -> EfiPartT { + EfiPartT::EfiPart(EfiPartition::new()) + } +} + +impl Default for EfiPartT { + fn default() -> Self { + EfiPartT::NoEFI + } +} + +impl Default for DistroKind { + fn default() -> Self { + DistroKind::Undefined + } +} + +impl BootPartDetails { + fn new() -> Self { + Self { + boot_part_fs: "".to_string(), + boot_part_path: "".to_string(), + boot_part_number: 0, + } + } +} + +impl RootPartDetails { + fn new() -> Self { + Self { + root_part_fs: "".to_string(), + root_part_path: "".to_string(), + root_part_number: 0, + } + } +} + +impl LVMDetails { + fn new() -> Self { + Self { + lvm_root_part: "".to_string(), + lvm_usr_part: "".to_string(), + lvm_var_part: "".to_string(), + } + } +} + +impl Distro { + pub fn new() -> Distro { + let mut partitions: Vec = Vec::new(); + //get_partitions is filling the variable partitions + get_partitions(&mut partitions); + let efi_part: EfiPartT = Default::default(); + let boot_part: BootPartDetails = BootPartDetails::new(); + let root_part: RootPartDetails = RootPartDetails::new(); + let kind: DistroKind = Default::default(); + let lvm_details = LVMDetails::new(); + let mut distro: Distro = Distro { + boot_part: boot_part, + rescue_root: root_part, + efi_part: efi_part, + is_lvm: false, + is_ade: false, + lvm_details: lvm_details, + kind: kind, + }; + + // here we start the core logic in order to determine what distro and type we have to cope with + dispatch(&partitions, &mut distro); + + distro + } +} + +fn get_partitions(partitions: &mut Vec) { + let link = read_link(constants::RESCUE_DISK); + let out = cmd_lib::run_fun!(parted -m ${link} print | grep -E "^ ?[0-9]{1,2} *"); + + match out { + Ok(v) => { + for line in v.lines() { + partitions.push(line.to_string()); + } + helper::log_info( + format!( + "We have the following partitions determined: {:?}", + partitions + ) + .as_str(), + ); + } + Err(e) => panic!("Fehler {:?}", e), + } +} + +// If there is only one partition detected +//fn do_old_ubuntu_or_centos(partition_info: &Vec, mut distro: &mut Distro) { +fn do_old_ubuntu_or_centos(partition_info: &[String], mut distro: &mut Distro) { + helper::log_info("This could be an old Ubuntu image or even an CentOS with one partition only"); + + // At first we have to determine whether this is a Ubuntu distro + // or whether it is a single partiton CentOs distro + if let Err(e) = mount::mkdir_assert() { + panic!("Creating assert directory is not possible : '{}'. ALAR is not able to proceed further",e); + } + + distro.rescue_root.root_part_fs = helper::get_partition_filesystem_detail(&partition_info[0]); + distro.rescue_root.root_part_number = helper::get_partition_number_detail(&partition_info[0]); + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + mount::mount_path_assert(distro.rescue_root.root_part_path.as_str()); + let pretty_name = helper::get_pretty_name("/tmp/assert/etc/os-release"); + mount::umount(constants::ASSERT_PATH); + + if pretty_name.contains("Debian") || pretty_name.contains("Ubuntu") { + distro.kind = DistroKind::Ubuntu; + let _ = mount::rmdir(constants::ASSERT_PATH); + } else { + // Single partiton CentOS + let _ = mount::rmdir(constants::ASSERT_PATH); + redhat::verify_redhat_nolvm(distro); + } +} + +// if we have two partition detected +//fn do_red_hat(partition_info: &Vec, distro: &mut Distro) { +fn do_red_hat(partition_info: &[String], distro: &mut Distro) { + helper::log_info("This could be a RedHat/Centos 6/7 image"); + redhat::do_redhat6_or_7(partition_info, distro); +} + +// if we have 3 partition detected +//fn do_recent_ubuntu(partition_info: &Vec, distro: &mut Distro) { +fn do_recent_ubuntu(partition_info: &[String], distro: &mut Distro) { + helper::log_info("This could be a recent Ubuntu 16.x or 18.x image"); + ubuntu::do_ubuntu(partition_info, distro); +} + +// if we have 4 partition detected +fn do_suse_or_lvm_or_ubuntu(partition_info: &[String], distro: &mut Distro) { + // This function is also called if we have an recent Ubuntu distro with ADE enabled + // With ADE a 4th partition got added to hold the boot-part-details plus luks + + // Define an enum which is used to decide which further part has to be executed + + enum Logic { + RedHat, + Suse, + Ubuntu, + } + let mut which_logic = Logic::RedHat; // Default value is Redhat + + // Not sure whether this is a RedHat or CENTOS with LVM or it is a Suse 12/15 instead + // Need to make a simple test + for partition in partition_info.iter() { + if partition.contains("lxboot") { + which_logic = Logic::Suse; + } + } + + // Verify if we have an Ubuntu distro with ADE enabled + // This needs to be verified first before we can do the RedHat part instead + // Since with ADE on Ubuntu we got a 4th partition added + if distro.is_ade { + let pretty_name = helper::get_pretty_name("/investigateroot/etc/os-release"); // This path must exists, otherwise it can not be determined + if pretty_name.is_empty() { + helper::log_error("'/investigationrooot' needs to be mounted first. ALAR does stop"); + process::exit(1); + } + if pretty_name.contains("Ubuntu") { + which_logic = Logic::Ubuntu; + } + } + + match which_logic { + Logic::RedHat => redhat::do_redhat_lvm_or(partition_info, distro), + Logic::Suse => suse::do_suse(partition_info, distro), + Logic::Ubuntu => ade::do_ubuntu_ade(partition_info, distro), + } +} + +//fn dispatch(partition_info: &Vec, mut distro: &mut Distro) { +fn dispatch(partition_info: &[String], mut distro: &mut Distro) { + // Test for an ADE repair environment + distro.is_ade = ade::is_ade_enabled(); + helper::log_info(format!("Ade is enabled : {}", distro.is_ade).as_str()); + + match partition_info.len() { + 1 => do_old_ubuntu_or_centos(partition_info, distro), + 2 => do_red_hat(partition_info, distro), + 3 => do_recent_ubuntu(partition_info, distro), + 4 => do_suse_or_lvm_or_ubuntu(partition_info, distro), + _ => { + helper::log_error("Unrecognized Linux distribution. ALAR tool is stopped\n + Your OS can not be determined. The OS distros supported are:\n + CentOS/Redhat 6.8 - 8.2\n + Ubuntu 16.4 LTS and Ubuntu 18.4 LTS\n + Suse 12 and 15\n + Debain 9 and 10\n + ALAR will stop!\n + If your OS is in the above list please report this issue at https://github.com/azure/repair-script-library/issues" + ); + + process::exit(1); + } + } +} diff --git a/src/linux/common/helpers/alar2/src/helper.rs b/src/linux/common/helpers/alar2/src/helper.rs new file mode 100644 index 00000000..f2d2f986 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/helper.rs @@ -0,0 +1,240 @@ +use crate::constants; +use crate::distro::{Distro, EfiPartT, EfiPartition}; +use crate::mount; +use chrono::prelude::Utc; +use std::process::Stdio; +use std::{fs, process}; +use cmd_lib::run_fun; + + +pub fn log_info(msg: &str) { + println!("[Info {}] {}", Utc::now(), msg); +} + +#[allow(dead_code)] +pub fn log_output(msg: &str) { + println!("[Output {}] {}", Utc::now(), msg); +} + +#[allow(dead_code)] +pub fn log_warning(msg: &str) { + println!("[Warning {}] {}", Utc::now(), msg); +} + +pub fn log_error(msg: &str) { + println!("[Error {}] {}", Utc::now(), msg); +} + +pub fn log_debug(msg: &str) { + println!("[Debug {}] {}", Utc::now(), msg); +} + +pub fn read_link(path: &str) -> String { + let real_path: String; + match fs::metadata(&path) { + Ok(_) => { + real_path = fs::canonicalize(&path) + .unwrap_or_default() + .as_os_str() + .to_str() + .unwrap_or_default() + .to_string(); + /* let os_option = fs::canonicalize(&path).ok().unwrap_or_default().as_os_str().to_str(); + if let Some(inner_real_path) = os_option { real_path = &inner_real_path.to_string(); } + */ + } + Err(_) => { + let message = format!("{} {}", "Path not found", &path); + log_error(&message); + panic!("Panic in function read_link"); + } + } + real_path +} + +pub(crate) fn cut<'a>(source: &'a str, delimiter: &str, field: usize) -> &'a str { + match source.split(delimiter).nth(field) { + Some(value) => value, + None => { + log_error("String not found. FATAL! ERROR NOT RECOVERABLE"); + panic!("Error in function cut"); + } + } +} + +pub fn get_partition_number_detail(source: &str) -> u8 { + cut(source, ":", 0).parse::().unwrap() +} + +pub fn get_partition_filesystem_detail(source: &str) -> String { + cut(source, ":", 4).to_string() +} + +pub(crate) fn get_pretty_name(path: &str) -> String { + let mut pretty_name: String = "".to_string(); + if let Ok(name) = run_fun!(grep -s PRETTY_NAME $path) { + pretty_name = cut(&name, "=", 1).to_string(); + } + pretty_name +} + +pub(crate) fn get_ade_mounpoint(source: &str) -> String { + let mut mountpoint = "".to_string(); + if let Ok(path) = cmd_lib::run_fun!(cat /proc/mounts | grep $source | cut -dr#" "# -f2) { + mountpoint = path; + } + log_info(format!("ADE mountpoint is: {}", &mountpoint).as_str()); + mountpoint +} + +pub(crate) fn fsck_partition(partition_path: &str, partition_filesystem: &str) { + // Need to handel the condition if no filesystem is available + // This can happen if we have a LVM partition + if partition_filesystem.is_empty() { + return; + } + + //let mut result: result::Result = Err(io::Error::new(io::ErrorKind::Other, "none")); // run_cmd returns "type CmdResult = Result<(), Error>;" + let mut exit_code = Some(0i32); + + match partition_filesystem { + "xfs" => { + log_info(format!("fsck for XFS on {}", partition_path).as_str()); + if let Err(e) = mount::mkdir_assert() { + panic!("Creating assert directory is not possible : '{}'. ALAR is not able to proceed further",e); + } + + // In case the filesystem has valuable metadata changes in a log which needs to + // be replayed. Mount the filesystem to replay the log, and unmount it before + // re-running xfs_repair + mount::mount_path_assert(partition_path); + mount::umount(constants::ASSERT_PATH); + + if let Ok(stat) = process::Command::new("xfs_repair") + .arg(&partition_path) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + { + exit_code = stat.code(); + } + } + "fat16" => { + log_info("fsck for fat16/vfat"); + if let Ok(stat) = process::Command::new("fsck.vfat") + .args(&["-p", &partition_path]) + .status() + { + exit_code = stat.code(); + } + } + _ => { + log_info(format!("fsck for {}", partition_filesystem).as_str()); + if let Ok(stat) = process::Command::new(format!("fsck.{}", partition_filesystem)) + .args(&["-p", &partition_path]) + .status() + { + exit_code = stat.code(); + } + } + } + + match exit_code { + // error 4 is returned by fsck.ext4 only + Some(_code @ 4) => { + log_error( + format!( + "Partition {} can not be repaired in auto mode", + &partition_path + ) + .as_str(), + ); + log_error("Aborting ALAR"); + process::exit(1); + } + // xfs_repair -n returns 1 if the fs is corrupted. + // Also fsck may raise this error but we ignore it as even a normal recover is raising it. FALSE-NEGATIVE + Some(_code @ 1) if partition_filesystem == "xfs" => { + log_error("A general error occured while trying to recover the device ${root_rescue}."); + log_error("Aborting ALAR"); + process::exit(1); + } + None => { + panic!( + "fsck operation terminated by signal error. ALAR is not able to proceed further!" + ); + } + + // Any other error stat is not of interest for us + _ => {} + } + + log_info("File system check finished"); +} + +pub(crate) fn set_efi_part_number_and_fs(distro: &mut Distro, partition: &str) { + let mut new_efi_part = EfiPartT::new(); + if let EfiPartT::EfiPart(EfiPartition { + efi_part_number: ref mut ref_to_number, + efi_part_fs: ref mut ref_to_efi_part_fs, + efi_part_path: _, + }) = new_efi_part + { + *ref_to_efi_part_fs = get_partition_filesystem_detail(partition); + *ref_to_number = get_partition_number_detail(partition); + } + distro.efi_part = new_efi_part; +} + +pub(crate) fn set_efi_part_path(distro: &mut Distro) { + // set_efi_part_path has to be used only after set_efi_part_number_and_fs has been called + let part_number = get_efi_part_number(distro); + if let EfiPartT::EfiPart(EfiPartition { + efi_part_number: _, + efi_part_fs: _, + efi_part_path: ref mut ref_to_efi_part_path, + }) = distro.efi_part + { + *ref_to_efi_part_path = + read_link(format!("{}{}", constants::LUN_PART_PATH, part_number).as_str()); + } +} + +pub(crate) fn get_efi_part_path(distro: &Distro) -> String { + let mut path: String = String::from(""); + if let EfiPartT::EfiPart(EfiPartition { + efi_part_number: _, + efi_part_fs: _, + efi_part_path: ref ref_to_efi_part_path, + }) = distro.efi_part + { + path = ref_to_efi_part_path.to_string(); + } + path +} + +pub(crate) fn get_efi_part_fs(distro: &Distro) -> String { + let mut fs: String = String::from(""); + if let EfiPartT::EfiPart(EfiPartition { + efi_part_number: _, + efi_part_fs: ref ref_to_efi_part_fs, + efi_part_path: _, + }) = distro.efi_part + { + fs = ref_to_efi_part_fs.to_string(); + } + fs +} + +fn get_efi_part_number(distro: &Distro) -> u8 { + let mut number: u8 = 0; + if let EfiPartT::EfiPart(EfiPartition { + efi_part_number: internal_number, + efi_part_fs: _, + efi_part_path: _, + }) = distro.efi_part + { + number = internal_number; + } + number +} diff --git a/src/linux/common/helpers/alar2/src/main.rs b/src/linux/common/helpers/alar2/src/main.rs new file mode 100644 index 00000000..21355f45 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/main.rs @@ -0,0 +1,82 @@ +mod action; +mod ade; +mod cli; +mod constants; +mod distro; +mod helper; +mod mount; +mod prepare_action; +mod redhat; +mod standalone; +mod suse; +mod ubuntu; + +use std::{ + process, +}; + +fn main() { + // First verify we have the right amount of information to operate + let cli_info = cli::cli(); + + // At first we need to verify the distro we have to work with + // the Distro struct does contain then all of the required information + let distro = distro::Distro::new(); + //eprintln!("{:?}", distro); + + // Do we have a valid distro or not? + + if distro.kind == distro::DistroKind::Undefined { + helper::log_error("Unrecognized Linux distribution. ALAR tool is stopped\n + Your OS can not be determined. The OS distros supported are:\n + CentOS/Redhat 6.8 - 8.2\n + Ubuntu 16.4 LTS and Ubuntu 18.4 LTS\n + Suse 12 and 15\n + Debain 9 and 10\n + ALAR will stop!\n + If your OS is in the above list please report this issue at https://github.com/azure/repair-script-library/issues" + ); + process::exit(1); + } + + // Prepare and mount the partitions. Take into account what distro we have to deal with + match mount::mkdir_rescue_root() { + Ok(_) => {} + Err(e) => panic!( + "The rescue-root dir can't be created. This is not recoverable! : {} ", + e + ), + } + + // Step 2 of prepare and mount. Mount the right dirs depending on the distro determined + prepare_action::distro_mount(&distro, &cli_info); + + // Verify we have an implementation available for the action to be executed + // Define a variable for the error condition that may happen + let mut is_action_error = false; + for action_name in cli_info.actions.split(',') { + match action::is_action_available(action_name) { + // Do the action + Ok( _is @ true) => { + match action::run_repair_script(&distro, action_name) { + Ok(_) => is_action_error = false, + Err(e) => {helper::log_error(format!("Action {} raised an error: '{}'", &action_name, e).as_str()); is_action_error=true;} + } + } + Ok( _is @ false) => {helper::log_error(format!("Action '{}' is not available", action_name).as_str()); is_action_error=true; } + Err(e) => { helper::log_error(format!("There was an error raised while verifying the action: '{}'", e).as_str()); is_action_error=true; } + } + } + + // Umount everything again + + + prepare_action::distro_umount(&distro); + + // Inform the calling process about the success + if is_action_error { + process::exit(1); + } else { + process::exit(0); + } +} diff --git a/src/linux/common/helpers/alar2/src/mount.rs b/src/linux/common/helpers/alar2/src/mount.rs new file mode 100644 index 00000000..8e3118b1 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/mount.rs @@ -0,0 +1,114 @@ +use crate::helper; +use crate::constants; +use std::{fs, io, process}; +//use sys_mount; + +pub(crate) fn mkdir_assert() -> Result<(), io::Error>{ + match fs::create_dir_all(constants::ASSERT_PATH) { + Ok(()) => Ok(()) , + Err(e) => {println!("Error while creating the assert directory: {}", e); + Err(e) + } + } +} + +pub(crate) fn mkdir_rescue_root() -> Result<(), io::Error>{ + match fs::create_dir_all(constants::RESCUE_ROOT) { + Ok(()) => Ok(()) , + Err(e) => {println!("Error while creating the rescue-root directory: {}", e); + Err(e) + } + } +} +fn mount( source: &str, destination: &str, option: Option<&str>) { + + // There is an issue on Ubuntu that the XFS filesystem is not enabled by default + // We need to load the driver first + match process::Command::new("modprobe").arg("xfs").status() { + Ok(_) => {}, + Err(_) => helper::log_error("Loading of the module xfs was not possible. This may result in mount issues! : "), + } + + let supported = match sys_mount::SupportedFilesystems::new() { + Ok(supported) => supported, + Err(_) => { + helper::log_error("Failed to get supported file systems"); + panic!(); + } + }; + + match sys_mount::Mount::new(source, destination, &supported, sys_mount::MountFlags::empty(), option) { + Ok(_) => { + helper::log_info(format!("mounted {} to {}", source, &destination).as_str() ); + } + Err(why) => { + helper::log_error(format!("failed to mount {} to {}: {}", source, destination, why).as_str()); + panic!(); + } + } +} + +pub(crate) fn bind_mount(source: &str, destination: &str) { + let supported = match sys_mount::SupportedFilesystems::new() { + Ok(supported) => supported, + Err(_) => { + helper::log_error("Failed to get supported file systems"); + panic!(); + } + }; + + match sys_mount::Mount::new(source, destination, &supported, sys_mount::MountFlags::BIND, None) { + Ok(_) => { + //helper::log_info(format!("mounted {} to {}", source, &destination).as_str() ); + } + Err(why) => { + helper::log_error(format!("failed to mount {} to {}: {}", source, destination, why).as_str()); + panic!(); + } + } +} + +pub(crate) fn mount_path_assert(source: &str) { + mount(source, constants::ASSERT_PATH, None); +} + +pub(crate) fn mount_root_on_rescue_root(root_source: &str, option: Option<&str>) { + mount(root_source, constants::RESCUE_ROOT, option); +} + +pub(crate) fn mount_boot_on_rescue_boot(boot_source: &str, option: Option<&str>) { + mount(boot_source, constants::RESCUE_ROOT_BOOT, option); +} + +pub(crate) fn mount_efi_on_rescue_efi(efi_source: &str, option: Option<&str>) { + mount(efi_source, constants::RESCUE_ROOT_BOOT_EFI, option); +} + +// Used only for LVM +pub(crate) fn mount_usr_on_rescue_root_usr(usr_source: &str) { + mount(usr_source, constants::RESCUE_ROOT_USR, None); +} + +// Used only for LVM +pub(crate) fn mount_var_on_rescue_root_var(var_source: &str) { + mount(var_source, constants::RESCUE_ROOT_VAR, None); +} + +pub(crate) fn umount(destination: &str) { + match sys_mount::unmount(destination, sys_mount::UnmountFlags::DETACH) { + Ok(()) => (), + Err(why) => { + helper::log_error(format!("Failed to unmount {}: {}", destination, why).as_str()); + helper::log_error("This shouldn't cause a sever issue for ALAR."); + } + } +} + +pub(crate) fn rmdir(path: &str) -> std::io::Result<()> { + fs::remove_dir_all(path)?; + Ok(()) +} + + + + diff --git a/src/linux/common/helpers/alar2/src/prepare_action.rs b/src/linux/common/helpers/alar2/src/prepare_action.rs new file mode 100644 index 00000000..bfb72dcf --- /dev/null +++ b/src/linux/common/helpers/alar2/src/prepare_action.rs @@ -0,0 +1,240 @@ +use crate::constants; +use crate::distro; +use crate::distro::DistroKind; +use crate::helper; +use crate::mount; +use crate::cli; +use crate::standalone; + +use fs_extra::dir; +use std::{env, fs, io, process}; +// use std::os::unix::fs::symlink as softlink; + +pub(crate) fn ubuntu_mount(distro: &distro::Distro) { + // We have to verify also whether we have old Ubuntus/Debian with one partition only + // Or whether we have also an EFI partition available + + mount::mount_root_on_rescue_root(distro.rescue_root.root_part_path.as_str(), None); + + // If ADE is enabled the extra boot partition needs to be mounted + if distro.is_ade { + mount::mount_boot_on_rescue_boot(distro.boot_part.boot_part_path.as_str(), None); + } + + let mount_option: Option<&str>; + if distro.efi_part != distro::EfiPartT::NoEFI { + if helper::get_efi_part_fs(&distro) == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + mount::mount_efi_on_rescue_efi(helper::get_efi_part_path(&distro).as_str(), mount_option); + } + + mount_support_filesystem(); + mount::bind_mount("/run", constants::RESCUE_ROOT_RUN); +} + +pub(crate) fn ubuntu_umount(distro: &distro::Distro) { + umount_support_filesystem(); + mount::umount(constants::RESCUE_ROOT_RUN); + + if distro.efi_part != distro::EfiPartT::NoEFI { + mount::umount(constants::RESCUE_ROOT_BOOT_EFI); + } + + // If ADE is enabled for Ubuntu the boot partition needs to be unmounted first + if distro.is_ade { + mount::umount(constants::RESCUE_ROOT_BOOT); + } + + mount::umount(constants::RESCUE_ROOT); +} + +pub(crate) fn suse_mount(distro: &distro::Distro) { + redhat_mount(distro); // We can use the same functionality +} + +pub(crate) fn suse_umount(distro: &distro::Distro) { + redhat_umount(distro); // we can use the same functionality +} + +pub(crate) fn redhat_mount(distro: &distro::Distro) { + if distro.is_lvm { + let mut mount_option: Option<&str>; + mount::mount_root_on_rescue_root(distro.lvm_details.lvm_root_part.as_str(), None); + mount::mount_usr_on_rescue_root_usr(distro.lvm_details.lvm_usr_part.as_str()); + mount::mount_var_on_rescue_root_var(distro.lvm_details.lvm_var_part.as_str()); + + if distro.boot_part.boot_part_fs == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + mount::mount_boot_on_rescue_boot(distro.boot_part.boot_part_path.as_str(), mount_option); + + if helper::get_efi_part_fs(&distro) == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + mount::mount_efi_on_rescue_efi(helper::get_efi_part_path(&distro).as_str(), mount_option); + + mount_support_filesystem(); + } else { + // if we have an XFS filesystem we have to set the 'nouuid' option + let mut mount_option: Option<&str>; + if distro.rescue_root.root_part_fs == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + + mount::mount_root_on_rescue_root(distro.rescue_root.root_part_path.as_str(), mount_option); + + if distro.boot_part.boot_part_fs == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + + mount::mount_boot_on_rescue_boot(distro.boot_part.boot_part_path.as_str(), mount_option); + + if distro.efi_part != distro::EfiPartT::NoEFI { + if helper::get_efi_part_fs(&distro) == "xfs" { + mount_option = Some("nouuid"); + } else { + mount_option = None; + } + mount::mount_efi_on_rescue_efi( + helper::get_efi_part_path(&distro).as_str(), + mount_option, + ); + } + mount_support_filesystem(); + } +} + +pub(crate) fn redhat6_mount(distro: &distro::Distro) { + mount::mount_root_on_rescue_root(distro.rescue_root.root_part_path.as_str(), None); + // In case we have no boot part information like on a single CentOS distro we don't need to mount boot + if distro.boot_part.boot_part_number != 0 { + mount::mount_boot_on_rescue_boot(distro.boot_part.boot_part_path.as_str(), None); + } + mount_support_filesystem(); +} + +pub(crate) fn redhat6_umount(distro: &distro::Distro) { + umount_support_filesystem(); + // In case we have no boot part information like on a single CentOS distro we don't need to umount boot + if distro.boot_part.boot_part_number != 0 { + mount::umount(constants::RESCUE_ROOT_BOOT); + } + mount::umount(constants::RESCUE_ROOT); +} + +pub(crate) fn redhat_umount(distro: &distro::Distro) { + if distro.is_lvm { + umount_support_filesystem(); + mount::umount(constants::RESCUE_ROOT_BOOT_EFI); + mount::umount(constants::RESCUE_ROOT_BOOT); + mount::umount(constants::RESCUE_ROOT_USR); + mount::umount(constants::RESCUE_ROOT_VAR); + mount::umount(constants::RESCUE_ROOT); + } else { + umount_support_filesystem(); + if distro.efi_part != distro::EfiPartT::NoEFI { + mount::umount(constants::RESCUE_ROOT_BOOT_EFI); + } + mount::umount(constants::RESCUE_ROOT_BOOT); + mount::umount(constants::RESCUE_ROOT); + } +} + +fn mount_support_filesystem() { + match mkdir_support_filesystems() { + Ok(()) => {} + Err(e) => panic!( + "Support Filesystems are not able to be created. This is not recoverable : {}", + e + ), + } + for fs in constants::SUPPORT_FILESYSTEMS.to_string().split(' ') { + mount::bind_mount( + format!("/{}/", fs).as_str(), + format!("{}{}", constants::RESCUE_ROOT, fs).as_str(), + ); + } +} +fn umount_support_filesystem() { + for fs in constants::SUPPORT_FILESYSTEMS.to_string().rsplit(' ') { + mount::umount(format!("{}{}", constants::RESCUE_ROOT, fs).as_str()); + } +} + +fn mkdir_support_filesystems() -> io::Result<()> { + for fs in constants::SUPPORT_FILESYSTEMS.to_string().split(' ') { + fs::create_dir_all(format!("{}{}", constants::RESCUE_ROOT, fs))?; + } + Ok(()) +} + + +pub(crate) fn distro_mount(distro: &distro::Distro, cli_info: &cli::CliInfo) { + match distro.kind { + DistroKind::Debian | DistroKind::Ubuntu => ubuntu_mount(&distro), + DistroKind::Suse => suse_mount(&distro), + DistroKind::RedHatCentOS => redhat_mount(&distro), + DistroKind::RedHatCentOS6 => redhat6_mount(&distro), + DistroKind::Undefined => {} // Nothing to do here we have covered this condition already + } + // Also copy the recovery scripts to /tmp in order to make them available for the chroot + // operation we do later + copy_actions_totmp(distro, cli_info); +} + +pub(crate) fn distro_umount(distro: &distro::Distro) { + match distro.kind { + DistroKind::Debian | DistroKind::Ubuntu => ubuntu_umount(&distro), + DistroKind::Suse => suse_umount(&distro), + DistroKind::RedHatCentOS => redhat_umount(&distro), + DistroKind::RedHatCentOS6 => redhat6_umount(&distro), + DistroKind::Undefined => {} // Nothing to do here we have covered this condition already + } +} + +fn copy_actions_totmp(distro: &distro::Distro, cli_info: &cli::CliInfo) { + // We need to copy the action scripts to /tmp + // This is the directory chroot can access + + if let Err(err) = fs::remove_dir_all(constants::ACTION_IMPL_DIR) { + println!("Directory {} can not be removed : '{}'", constants::ACTION_IMPL_DIR, err ); + //distro_umount(distro); + //process::exit(1); + } + if !cli_info.standalone { + let mut options = dir::CopyOptions::new(); //Initialize default values for CopyOptions + options.skip_exist = true; + + match env::current_dir() { + Ok(cd) => println!("The current dir is : {}", cd.display() ), + Err(e) => println!("Error : {}", e), + } + + // base directory already set correct by linux-alar2.sh + match dir::copy("src/action_implementation", "/tmp", &options) { + Ok(_) => {}, + Err(e) => { + println!("Copy operation for action_implementation directory failed. ALAR needs to stop: {}", e); + distro_umount(distro); + process::exit(1); + } + } + + +} else if let Err(e) = standalone::download_action_scripts(cli_info) { + distro_umount(distro); + panic!("action scripts are not able to be copied or downloadable : '{}'", e); +} + +} diff --git a/src/linux/common/helpers/alar2/src/redhat.rs b/src/linux/common/helpers/alar2/src/redhat.rs new file mode 100644 index 00000000..ec81555c --- /dev/null +++ b/src/linux/common/helpers/alar2/src/redhat.rs @@ -0,0 +1,334 @@ +#![allow(non_snake_case)] +use std::{fs, process}; + +use crate::ade; +use crate::constants; +use crate::distro; +use crate::helper; +use crate::mount; + +use cmd_lib::{run_cmd, run_fun}; + + +pub(crate) fn do_redhat_lvm_or(partition_info: &[String], distro: &mut distro::Distro) { + let mut contains_lvm_partition: bool = false; + + for partition in partition_info.iter() { + if partition.contains("lvm") { + contains_lvm_partition = true; + } + } + + if contains_lvm_partition { + do_redhat_lvm(partition_info, distro); + } else if partition_info.len() == 1 { + do_centos_single_partition(distro); + } else { + do_redhat_nolvm(partition_info, distro); + } +} + +pub(crate) fn do_redhat6_or_7(partition_info: &[String], mut distro: &mut distro::Distro) { + if !distro.is_ade { + for partition in partition_info { + if helper::get_partition_number_detail(partition) == 1 { + distro.boot_part.boot_part_number = 1; + distro.boot_part.boot_part_fs = helper::get_partition_filesystem_detail(partition); + } + + if helper::get_partition_number_detail(partition) == 2 { + distro.rescue_root.root_part_number = 2; + distro.rescue_root.root_part_fs = + helper::get_partition_filesystem_detail(partition); + } + } + + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + + verify_redhat_nolvm(distro); + } else { + // ADE part + ade::do_redhat6_or_7_ade( distro); + } +} + +fn do_redhat_nolvm(partition_info: &[String], mut distro: &mut distro::Distro) { + if !distro.is_ade { + // 4 partitions with no LVM we find on an 'CentOS Linux release 7.7.1908' for instance + /* + Number Start End Size File system Name Flags + 14 1049kB 5243kB 4194kB bios_grub + 15 5243kB 524MB 519MB fat16 EFI System Partition boot + 1 525MB 1050MB 524MB xfs + 2 1050MB 32.2GB 31.2GB xfs + */ + + helper::log_info( + "This is a recent RedHat or CentOS image with 4 partitions and no LVM signature", + ); + + distro.is_lvm = false; + + // Unfortunately we need to work with hardcoded values as there exist no label information + distro.boot_part.boot_part_fs = "xfs".to_string(); + distro.boot_part.boot_part_number = 1; + + distro.rescue_root.root_part_fs = "xfs".to_string(); + distro.rescue_root.root_part_number = 2; + + //For EFI partition we use normal logic in order to setup the details correct + for partition in partition_info.iter() { + if partition.contains("EFI") { + helper::set_efi_part_number_and_fs(&mut distro, &partition); + } + } + + // In the next steps we have to set the partition path correct and do a fsck on them + + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + + helper::set_efi_part_path(&mut distro); + + helper::fsck_partition( + helper::get_efi_part_path(&distro).as_str(), + helper::get_efi_part_fs(&distro).as_str(), + ); + + verify_redhat_nolvm(distro); + } else { + // This an ADE enabled OS + + helper::log_info( + "This is a recent RedHat or CentOS image with 4 partitions and no LVM signature", + ); + helper::log_info("An ADE signature got identified"); + distro.is_lvm = false; + ade::do_redhat_nolvm_ade(partition_info, distro); + } +} + +fn do_redhat_lvm(partition_info: &[String], mut distro: &mut distro::Distro) { + helper::log_info("This is a recent RedHat or CentOS image with 4 partitions and LVM signature"); + distro.is_lvm = true; + + // At first we need to prepare the LVM setup + match run_cmd!(pvscan -q -q; vgscan -q -q; lvscan -q -q;) { + Ok(_) => {} + Err(error) => panic!("There is a problem to setup LVM correct. {}", error), + } + + if !distro.is_ade { + // TMP is required for the macro run_fun! or run_cmd! + let TMP = constants::PARTITION_TMP; + + for partition in partition_info { + let _ = run_cmd!(echo $partition > $TMP); + if let Ok(name) = run_fun!(grep -s -v EFI $TMP | grep -v lvm | grep -v bios) { + helper::log_debug(&name); + distro.boot_part.boot_part_fs = + helper::get_partition_filesystem_detail(&name.as_str()); + distro.boot_part.boot_part_number = + helper::get_partition_number_detail(&name.as_str()); + } + + if let Ok(name) = run_fun!(grep -s lvm $TMP) { + helper::log_debug(&name); + distro.rescue_root.root_part_fs = + helper::get_partition_filesystem_detail(&name.as_str()); + distro.rescue_root.root_part_number = + helper::get_partition_number_detail(&name.as_str()); + } + + if partition.contains("EFI") { + helper::log_debug(format!("UEFI partition is '{}'", &partition).as_str()); + helper::set_efi_part_number_and_fs(&mut distro, &partition); + } + } + + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + + helper::set_efi_part_path(&mut distro); + + helper::fsck_partition( + helper::get_efi_part_path(&distro).as_str(), + helper::get_efi_part_fs(&distro).as_str(), + ); + + // Set the path details for later usage + distro.lvm_details.lvm_root_part = lvm_path_helper("rootlv"); + distro.lvm_details.lvm_usr_part = lvm_path_helper("usrlv"); + distro.lvm_details.lvm_var_part = lvm_path_helper("varlv"); + + verify_redhat_lvm(distro); + } else { + // Ade part + helper::log_info( + "This is a recent RedHat or CentOS image with 4 partitions and LVM signature", + ); + helper::log_info("An ADE signature got identified"); + + ade::do_redhat_lvm_ade(partition_info, distro); + } +} + +// verify_redhat_nolvm does set the DistroKind to either RedHatCentOS or RedHatCentOS6 +// if the verification is succesful +pub(crate) fn verify_redhat_nolvm(distro: &mut distro::Distro) { + if let Err(e) = mount::mkdir_assert() { + panic!( + "Creating assert directory is not possible : {}. ALAR is not able to proceed further", + e + ); + } + + mount::mount_path_assert(distro.rescue_root.root_part_path.as_str()); + + set_redhat_kind(distro); + + mount::umount(constants::ASSERT_PATH); + if mount::rmdir(constants::ASSERT_PATH).is_err() { + helper::log_debug("ASSERT_PATH can not be removed. This is a minor issue. ALAR is able to continue further"); + } +} + +pub(crate) fn verify_redhat_lvm(distro: &mut distro::Distro) { + mount::mount_path_assert(distro.lvm_details.lvm_root_part.as_str()); + + set_redhat_kind(distro); + + mount::umount(constants::ASSERT_PATH); +} + +fn set_redhat_kind(mut distro: &mut distro::Distro) { + let mut pretty_name = helper::get_pretty_name(constants::OS_RELEASE); + if pretty_name.is_empty() { + // if len is 0 then it points to a RedHat or CentOS 6 distro + // let us read the correct file instead + match fs::read_to_string(constants::REDHAT_RELEASE) { + Ok(value) => pretty_name = value, + Err(_) => { + helper::log_error( "It is not possible to determine the OS kind. ALAR is not able to proceed further"); + process::exit(1); + } + } + if pretty_name.contains("CentOS") || pretty_name.contains("Red Hat") { + helper::log_info(format!("Pretty Name is : {}", &pretty_name).as_str()); + distro.kind = distro::DistroKind::RedHatCentOS6; + } + } else if pretty_name.contains("CentOS") || pretty_name.contains("Red Hat") { + helper::log_info(format!("Pretty Name is : {}", &pretty_name).as_str()); + distro.kind = distro::DistroKind::RedHatCentOS; + } +} + +pub(crate) fn lvm_path_helper(lvname: &str) -> String { + let mut lvpath: String = "".to_string(); + if let Ok(value) = run_fun!(lvscan | grep $lvname) { + if let Some(path) = value.split('\'').nth(1) { + lvpath = path.to_string(); + } + } + lvpath +} + +fn _lvm_get_filesystem(lvpath: &str) -> String { + let mut filesystem = "".to_string(); + if let Ok(value) = run_fun!(parted -m $lvpath print | grep -E "^ ?[0-9]{1,2} *") { + if let Some(path) = value.split(':').nth(4) { + filesystem = path.to_string(); + } + } + filesystem +} + +pub(crate) fn do_centos_single_partition(mut distro: &mut distro::Distro) { + // It is safe to use hardcoded values + distro.rescue_root.root_part_path = + helper::read_link(format!("{}{}", constants::LUN_PART_PATH, 1).as_str()); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + // We have a single partition only boot and efi partitions do not need to be set + verify_redhat_nolvm(distro); +} diff --git a/src/linux/common/helpers/alar2/src/standalone.rs b/src/linux/common/helpers/alar2/src/standalone.rs new file mode 100644 index 00000000..4f2dfc8b --- /dev/null +++ b/src/linux/common/helpers/alar2/src/standalone.rs @@ -0,0 +1,45 @@ +use cmd_lib; +use crate::helper; +use crate::cli; +use crate::constants; +use std::{io,process,fs}; +use fs_extra; + +pub(crate) fn download_action_scripts(cli_info: &cli::CliInfo) -> io::Result<()> { + if cli_info.action_directory.is_empty() { + // First download the git archive + // Process::Command used in order to ensure we finish the download process + if let Ok(mut child) = process::Command::new("curl").args(&["-o","/tmp/alar2.tar.gz","-L","https://api.github.com/repos/Azure/repair-script-library/tarball/alar2-test"]).spawn() { + child.wait().expect("Archive alar2.tar.gz not downloaded"); + } else { + helper::log_error("Command curl not executed"); + process::exit(1); + } + + // Expand the action_implementation directory + cmd_lib::run_cmd!(tar --wildcards --strip-component=7 -xzf /tmp/alar2.tar.gz -C /tmp *src/linux/common/helpers/alar2/src/action_implementation)?; + + // Get two further files + //cmd_lib::run_cmd!(tar --wildcards --strip-component=1 -xzf /tmp/alar2.tar.gz -C /tmp *src/linux/common/helpers/Logger.sh)?; + //cmd_lib::run_cmd!(tar --wildcards --strip-component=1 -xzf /tmp/alar2.tar.gz -C /tmp *src/linux/common/setup/init.sh)?; + Ok(()) +} else { + // In case we have a local directory for our action scripts we need to copy the actions to + // tmp/action_implementation + if let Err(e) = load_local_action(cli_info.action_directory.as_str()) { + return Err(io::Error::new(io::ErrorKind::Other, format!("Load local action failed : '{}'",e))); + } + Ok(()) +} +} + +fn load_local_action(directory_source: &str) -> fs_extra::error::Result { + let _ = fs::remove_dir_all(constants::ACTION_IMPL_DIR); + let mut options = fs_extra::dir::CopyOptions::new(); + options.skip_exist = true; + options.copy_inside = true; + match fs_extra::dir::copy(directory_source, constants::ACTION_IMPL_DIR, &options) { + Ok(v) => Ok(v), + Err(e) => Err(e), + } +} \ No newline at end of file diff --git a/src/linux/common/helpers/alar2/src/suse.rs b/src/linux/common/helpers/alar2/src/suse.rs new file mode 100644 index 00000000..01d32ecb --- /dev/null +++ b/src/linux/common/helpers/alar2/src/suse.rs @@ -0,0 +1,68 @@ +use crate::constants; +use crate::distro; +use crate::helper; + +pub(crate) fn do_suse(partition_info: &[String], mut distro: &mut distro::Distro) { + if !distro.is_ade { + distro.kind = distro::DistroKind::Suse; + + for partition in partition_info { + if partition.contains("lxboot") { + distro.boot_part.boot_part_fs = + helper::get_partition_filesystem_detail(partition.as_str()); + distro.boot_part.boot_part_number = + helper::get_partition_number_detail(partition.as_str()); + } + + if partition.contains("lxroot") { + distro.rescue_root.root_part_fs = + helper::get_partition_filesystem_detail(partition.as_str()); + distro.rescue_root.root_part_number = + helper::get_partition_number_detail(partition.as_str()); + } + + if partition.contains("UEFI") { + helper::set_efi_part_number_and_fs(&mut distro, &partition); + } + } + + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.boot_part.boot_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + + helper::set_efi_part_path(&mut distro); + + helper::fsck_partition( + helper::get_efi_part_path(&distro).as_str(), + helper::get_efi_part_fs(&distro).as_str(), + ); + } else { + // ADE part + // Not yet available + // See --> https://docs.microsoft.com/en-us/azure/virtual-machines/linux/disk-encryption-overview + } +} diff --git a/src/linux/common/helpers/alar2/src/ubuntu.rs b/src/linux/common/helpers/alar2/src/ubuntu.rs new file mode 100644 index 00000000..47f6fb94 --- /dev/null +++ b/src/linux/common/helpers/alar2/src/ubuntu.rs @@ -0,0 +1,96 @@ +#![allow(non_snake_case)] + +use crate::constants; +use crate::distro; +use crate::helper; +use crate::mount; +use cmd_lib::{run_cmd, run_fun}; + +pub(crate) fn verify_ubuntu(mut distro: &mut distro::Distro) { + + if let Err(e) = mount::mkdir_assert() { + panic!("Creating assert directory is not possible: {} ALAR is not able to proceed further", e); + } + + mount::mount_path_assert(distro.rescue_root.root_part_path.as_str()); + let pretty_name = helper::get_pretty_name(constants::OS_RELEASE); + mount::umount(constants::ASSERT_PATH); + + if mount::rmdir(constants::ASSERT_PATH).is_err() { + helper::log_debug("ASSERT_PATH can not be removed. This is a minor issue. ALAR is able to continue further"); + } + + println!("pretty : {}", &pretty_name); + if pretty_name.contains("Ubuntu") { + distro.kind = distro::DistroKind::Ubuntu; + } + + if pretty_name.contains("Debian") { + distro.kind = distro::DistroKind::Debian; + } +} + +pub(crate) fn do_ubuntu(partition_info: &[String], mut distro: &mut distro::Distro) { + for partition in partition_info { + // TMP is required for the macro run_fun! or run_cmd! + let _TMP = constants::PARTITION_TMP; + + // Write the partition details into the file referenced in _TMP + let _ = run_cmd!(echo $partition > $_TMP); + if let Ok(name) = run_fun!(grep -s -v boot $_TMP | grep -v bios) { + println!("{}", name); + distro.rescue_root.root_part_fs = helper::get_partition_filesystem_detail(&name.as_str()); + distro.rescue_root.root_part_number = helper::get_partition_number_detail(&name.as_str()); + } + + if partition.contains("boot") { + helper::set_efi_part_number_and_fs(&mut distro, partition); + } + } + + distro.rescue_root.root_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + + + helper::fsck_partition( + distro.rescue_root.root_part_path.as_str(), + distro.rescue_root.root_part_fs.as_str(), + ); + + helper::set_efi_part_path(&mut distro); + helper::fsck_partition( + helper::get_efi_part_path(&distro).as_str(), + helper::get_efi_part_fs(&distro).as_str(), + ); + + // If we have ADE enabled on the disk to be recovered then there is a 4th partition to hold the boot + // part information + if distro.is_ade { + // We use hardcoded values in this case + distro.boot_part.boot_part_fs = "ext2".to_string(); + distro.boot_part.boot_part_number = 2 ; + + distro.boot_part.boot_part_path = helper::read_link( + format!( + "{}{}", + constants::LUN_PART_PATH, + distro.rescue_root.root_part_number + ) + .as_str(), + ); + + helper::fsck_partition( + distro.boot_part.boot_part_path.as_str(), + distro.boot_part.boot_part_fs.as_str(), + ); + } + + verify_ubuntu(distro); +} diff --git a/src/linux/linux-alar2.sh b/src/linux/linux-alar2.sh new file mode 100644 index 00000000..b839b3b6 --- /dev/null +++ b/src/linux/linux-alar2.sh @@ -0,0 +1,14 @@ +#!/bin/bash +. ./src/linux/common/setup/init.sh + +Log-Output "Starting the recovery" +cd ./src/linux/common/helpers/alar2 +/root/.cargo/bin/cargo build -q --release +mkdir bin +cp target/release/alar2 bin/ +chmod 700 ./bin/alar2 +./bin/alar2 $1 +# Save the error state from alar2 +error_state=$? +Log-Output "Recovery script finished" +exit $error_state