diff --git a/SECURITY.md b/.github/SECURITY.md similarity index 100% rename from SECURITY.md rename to .github/SECURITY.md diff --git a/.github/workflows/bashisms.yml b/.github/workflows/bashisms.yml index e667f6d23..cffacb044 100644 --- a/.github/workflows/bashisms.yml +++ b/.github/workflows/bashisms.yml @@ -1,10 +1,10 @@ name: Check for bashisms on: - push: - paths: - - tabs/** - branches: [ "main" ] + # push: + # paths: + # - tabs/** + # branches: [ "main" ] pull_request: paths: - tabs/** @@ -16,11 +16,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - run: git fetch origin ${{ github.base_ref }} - name: Install devscripts run: sudo apt-get update && sudo apt-get install devscripts - - name: Check for bashisms - working-directory: tabs - run: find . -name '*.sh' | xargs -P 4 -n 1 checkbashisms + - name: Check for bashisms in changed files + run: | + for file in $(git diff --name-only origin/${{ github.base_ref }} HEAD tabs); do + if [[ "$file" == *.sh ]]; then + checkbashisms "$file" + fi + done diff --git a/.github/workflows/issue-slash-commands.yaml b/.github/workflows/issue-slash-commands.yaml index 384cbd9de..e15f7257d 100644 --- a/.github/workflows/issue-slash-commands.yaml +++ b/.github/workflows/issue-slash-commands.yaml @@ -28,7 +28,7 @@ jobs: id: check_user if: env.comment == 'true' run: | - ALLOWED_USERS=("ChrisTitusTech" "afonsofrancof" "Marterich" "MyDrift-user" "Real-MullaC") + ALLOWED_USERS=("ChrisTitusTech" "afonsofrancof" "Marterich" "MyDrift-user" "Real-MullaC" "nnyyxxxx" "adamperkowski" "lj3954" "jeevithakannan2") if [[ " ${ALLOWED_USERS[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then echo "user=true" >> $GITHUB_ENV else diff --git a/.github/workflows/linutil.yml b/.github/workflows/linutil.yml index ef6b2c109..94126f647 100644 --- a/.github/workflows/linutil.yml +++ b/.github/workflows/linutil.yml @@ -44,9 +44,13 @@ jobs: - name: Build x86_64 binary run: cargo build --target-dir=build --release --verbose --target=x86_64-unknown-linux-musl + - name: Build aarch64 binary + run: cross build --target-dir=build --release --verbose --target=aarch64-unknown-linux-musl + - name: Move binaries to build directory run: | mv build/x86_64-unknown-linux-musl/release/linutil build/linutil + mv build/aarch64-unknown-linux-musl/release/linutil build/linutil-aarch64 - name: Pull latest changes run: | @@ -57,7 +61,7 @@ jobs: - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Commit Linutil - file_pattern: "build/linutil" + file_pattern: "build/linutil build/linutil-aarch64" add_options: '--force' if: success() @@ -87,9 +91,12 @@ jobs: ${{ steps.generate_notes.outputs.body }} ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil) + ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil-aarch64) + append_body: false files: | ./build/linutil + ./build/linutil-aarch64 ./start.sh ./startdev.sh prerelease: true diff --git a/core/Cargo.toml b/core/Cargo.toml index ebe1091df..fdabe6091 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true [dependencies] include_dir = "0.7.4" tempdir = "0.3.7" -serde = { version = "1.0.205", features = ["derive"] } -toml = "0.8.19" +serde = { version = "1.0.205", features = ["derive"], default-features = false } +toml = { version = "0.8.19", features = ["parse"], default-features = false } which = "6.0.3" ego-tree = { workspace = true } diff --git a/core/src/inner.rs b/core/src/inner.rs index e965a3725..f936e3b2c 100644 --- a/core/src/inner.rs +++ b/core/src/inner.rs @@ -21,16 +21,29 @@ pub fn get_tabs(validate: bool) -> Vec { }); let tabs: Vec = tabs - .map(|(TabEntry { name, data }, directory)| { - let mut tree = Tree::new(ListNode { - name: "root".to_string(), - description: "".to_string(), - command: Command::None, - }); - let mut root = tree.root_mut(); - create_directory(data, &mut root, &directory); - Tab { name, tree } - }) + .map( + |( + TabEntry { + name, + data, + multi_selectable, + }, + directory, + )| { + let mut tree = Tree::new(ListNode { + name: "root".to_string(), + description: String::new(), + command: Command::None, + }); + let mut root = tree.root_mut(); + create_directory(data, &mut root, &directory); + Tab { + name, + tree, + multi_selectable, + } + }, + ) .collect(); if tabs.is_empty() { @@ -48,6 +61,12 @@ struct TabList { struct TabEntry { name: String, data: Vec, + #[serde(default = "default_multi_selectable")] + multi_selectable: bool, +} + +fn default_multi_selectable() -> bool { + true } #[derive(Deserialize)] diff --git a/core/src/lib.rs b/core/src/lib.rs index 164e4e42a..1d52116b8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -16,6 +16,7 @@ pub enum Command { pub struct Tab { pub name: String, pub tree: Tree, + pub multi_selectable: bool, } #[derive(Clone, Hash, Eq, PartialEq)] diff --git a/docs/userguide.md b/docs/userguide.md index 8fc385f58..d17155689 100644 --- a/docs/userguide.md +++ b/docs/userguide.md @@ -21,6 +21,7 @@ ## Applications Setup +- **Flathub**: Installs / Uninstalls Flatpak and Flathub. - **Alacritty Setup**: Installs and configures Alacritty for you. - **DwmTitus Setup**: Sets up the Dwm window manager. - **Kitty Setup**: Installs and configures Kitty for you. @@ -30,3 +31,11 @@ ## Security Features - **Firewall Baselines**: Sets up firewall rules. + +## Utilities + +- **Monitor Control**: Controls monitor settings on X11. +- **Bluetooth Control**: Controls Bluetooth settings. +- **Wifi Control**: Controls WiFi settings. +- **Numlock Control**: Sets up Numlock on boot. +- **User Account Manager**: Manage users and groups. diff --git a/start.sh b/start.sh index b6fe4fdc8..fd69cbece 100755 --- a/start.sh +++ b/start.sh @@ -1,5 +1,7 @@ #!/bin/sh -e +# Prevent execution if this script was only partially downloaded +{ rc='\033[0m' red='\033[0;31m' @@ -46,3 +48,4 @@ check $? "Executing linutil" rm -f "$temp_file" check $? "Deleting the temporary file" +} # End of wrapping diff --git a/startdev.sh b/startdev.sh index 0df8ce53a..5fb2f1b2b 100755 --- a/startdev.sh +++ b/startdev.sh @@ -1,5 +1,7 @@ #!/bin/sh -e +# Prevent execution if this script was only partially downloaded +{ RC='\033[0m' RED='\033[0;31m' @@ -73,3 +75,4 @@ check $? "Executing linutil" rm -f $TMPFILE check $? "Deleting the temporary file" +} # End of wrapping diff --git a/tabs/applications-setup/alacritty-setup.sh b/tabs/applications-setup/alacritty-setup.sh index 2d9143401..83eeb986b 100755 --- a/tabs/applications-setup/alacritty-setup.sh +++ b/tabs/applications-setup/alacritty-setup.sh @@ -25,6 +25,7 @@ setupAlacrittyConfig() { fi mkdir -p "${HOME}/.config/alacritty/" curl -sSLo "${HOME}/.config/alacritty/alacritty.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/alacritty.toml" + curl -sSLo "${HOME}/.config/alacritty/keybinds.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/keybinds.toml" curl -sSLo "${HOME}/.config/alacritty/nordic.toml" "https://github.com/ChrisTitusTech/dwm-titus/raw/main/config/alacritty/nordic.toml" printf "%b\n" "${GREEN}Alacritty configuration files copied.${RC}" } @@ -32,4 +33,4 @@ setupAlacrittyConfig() { checkEnv checkEscalationTool installAlacritty -setupAlacrittyConfig \ No newline at end of file +setupAlacrittyConfig diff --git a/tabs/applications-setup/bottles-setup.sh b/tabs/applications-setup/bottles-setup.sh new file mode 100644 index 000000000..8d51d484f --- /dev/null +++ b/tabs/applications-setup/bottles-setup.sh @@ -0,0 +1,14 @@ +#!/bin/sh -e + +. ../common-script.sh + +install_bottles() { + printf "%b\n" "${YELLOW}Installing Bottles...${RC}" + . ./setup-flatpak.sh + flatpak install -y flathub com.usebottles.bottles + printf "%b\n" "${GREEN}Bottles installed successfully. Restart the system to apply changes...${RC}" +} + +checkEnv +checkEscalationTool +install_bottles2 \ No newline at end of file diff --git a/tabs/applications-setup/browser-setup.sh b/tabs/applications-setup/browser-setup.sh new file mode 100644 index 000000000..3e7f5e20e --- /dev/null +++ b/tabs/applications-setup/browser-setup.sh @@ -0,0 +1,234 @@ +#!/bin/sh -e + +. ../common-script.sh + +install_chrome() { + printf "%b\n" "${YELLOW}Installing Google Chrome..${RC}." + case "$PACKAGER" in + apt-get|nala) + wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + "$ESCALATION_TOOL" dpkg -i google-chrome-stable_current_amd64.deb + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER" addrepo http://dl.google.com/linux/chrome/rpm/stable/x86_64 Google-Chrome + "$ESCALATION_TOOL" "$PACKAGER" refresh + "$ESCALATION_TOOL" "$PACKAGER" --non-interactive install google-chrome-stable + ;; + pacman) + "$AUR_HELPER" -S --noconfirm google-chrome + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install fedora-workstation-repositories + "$ESCALATION_TOOL" "$PACKAGER" config-manager --set-enabled google-chrome + "$ESCALATION_TOOL" "$PACKAGER" install google-chrome-stable + ;; + *) + printf "%b\n" "${RED}The script does not support your Distro. Install manually..${RC}" + ;; + esac + +} + +install_thorium() { + printf "%b\n" "${YELLOW}Installing Thorium Browser...${RC}" + case "$PACKAGER" in + apt-get|nala) + "$ESCALATION_TOOL" rm -fv /etc/apt/sources.list.d/thorium.list + "$ESCALATION_TOOL" wget --no-hsts -P /etc/apt/sources.list.d/ http://dl.thorium.rocks/debian/dists/stable/thorium.list + "$ESCALATION_TOOL" "$PACKAGER" update + "$ESCALATION_TOOL" "$PACKAGER" install -y thorium-browser + ;; + zypper|dnf) + url=$(curl -s https://api.github.com/repos/Alex313031/Thorium/releases/latest | grep -oP '(?<=browser_download_url": ")[^"]*\.rpm') + echo $url && curl -L $url -o thorium-latest.rpm + "$ESCALATION_TOOL" rpm -i thorium-latest.rpm && rm thorium-latest.rpm + ;; + pacman) + "$AUR_HELPER" -S --needed --noconfirm thorium-browser-bin + ;; + *) + printf "%b\n" "${RED}Unsupported package manager. Please install Thorium manually.${RC}" + exit 1 + ;; + esac + printf "%b\n" "${GREEN}Thorium Browser installed successfully!${RC}" +} + +install_firefox() { + printf "%b\n" "${YELLOW}Installing Mozilla Firefox...${RC}" + case "$PACKAGER" in + apt-get) + "$ESCALATION_TOOL" "$PACKAGER" update + "$ESCALATION_TOOL" "$PACKAGER" install -y firefox + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER" --non-interactive install MozillaFirefox + ;; + pacman) + "$ESCALATION_TOOL" "$PACKAGER" -S --noconfirm firefox + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install firefox + ;; + *) + printf "%b\n" "${RED}The script does not support your Distro. Install manually..${RC}" + ;; + esac + +} + +install_librewolf() { + printf "%b\n" "${YELLOW}Installing Librewolf...${RC}" + case "$PACKAGER" in + apt-get|nala) + "$ESCALATION_TOOL" "$PACKAGER" update && "$ESCALATION_TOOL" "$PACKAGER" install -y wget gnupg lsb-release apt-transport-https ca-certificates + distro=`if echo " una bookworm vanessa focal jammy bullseye vera uma " | grep -q " $(lsb_release -sc) "; then lsb_release -sc; else echo focal; fi` + wget -O- https://deb.librewolf.net/keyring.gpg | "$ESCALATION_TOOL" gpg --dearmor -o /usr/share/keyrings/librewolf.gpg + echo "Types: deb +URIs: https://deb.librewolf.net +Suites: $distro +Components: main +Architectures: amd64 +Signed-By: /usr/share/keyrings/librewolf.gpg" | "$ESCALATION_TOOL" tee /etc/apt/sources.list.d/librewolf.sources > /dev/null + "$ESCALATION_TOOL" "$PACKAGER" update && "$ESCALATION_TOOL" "$PACKAGER" install -y librewolf + ;; + dnf) + curl -fsSL https://rpm.librewolf.net/librewolf-repo.repo | pkexec tee /etc/yum.repos.d/librewolf.repo > /dev/null + "$ESCALATION_TOOL" "$PACKAGER" install -y librewolf + ;; + rpm-ostree) + rpm-ostree install -y librewolf + ;; + zypper) + "$ESCALATION_TOOL" rpm --import https://rpm.librewolf.net/pubkey.gpg + "$ESCALATION_TOOL" zypper ar -ef https://rpm.librewolf.net librewolf + "$ESCALATION_TOOL" zypper ref + "$ESCALATION_TOOL" zypper in librewolf + ;; + pacman) + "$AUR_HELPER" -S --needed --noconfirm librewolf-bin + ;; + *) + printf "%b\n" "${RED}Unsupported package manager. Please install Librewolf manually.${RC}" + exit 1 + ;; + esac + printf "%b\n" "${GREEN}Librewolf installed successfully!${RC}" +} + +install_brave() { + printf "%b\n" "${YELLOW}Installing Brave...${RC}" + case "$PACKAGER" in + apt-get|nala) + "$ESCALATION_TOOL" "$PACKAGER" install curl + "$ESCALATION_TOOL" curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg + echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list + "$ESCALATION_TOOL" "$PACKAGER" update + "$ESCALATION_TOOL" "$PACKAGER" install brave-browser + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER"install curl + "$ESCALATION_TOOL" rpm --import https://brave-browser-rpm-release.s3.brave.com/brave-core.asc + "$ESCALATION_TOOL" "$PACKAGER" addrepo https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo + "$ESCALATION_TOOL" "$PACKAGER" --non-interactive install brave-browser + ;; + pacman) + "$AUR_HELPER" -S --noconfirm brave-bin + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install dnf-plugins-core + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo + "$ESCALATION_TOOL" rpm --import https://brave-browser-rpm-release.s3.brave.com/brave-core.asc + "$ESCALATION_TOOL" "$PACKAGER" install brave-browser + ;; + *) + printf "%b\n" "${RED}The script does not support your Distro. Install manually..${RC}" + ;; + esac +} + +install_vivaldi() { + printf "%b\n" "${YELLOW}Installing Vivaldi...${RC}" + wget https://downloads.vivaldi.com/snapshot/install-vivaldi.sh + sh install-vivaldi.sh +} + +install_chromium() { + printf "%b\n" "${YELLOW}Installing Chromium...${RC}" + case "$PACKAGER" in + apt-get|nala) + "$ESCALATION_TOOL" "$PACKAGER" install -y chromium + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER" --non-interactive install chromium + ;; + pacman) + "$ESCALATION_TOOL" "$PACKAGER" -S --noconfirm chromium + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install --assumeyes https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm + "$ESCALATION_TOOL" "$PACKAGER" install chromium + ;; + *) + printf "%b\n" "${RED}The script does not support your Distro. Install manually..${RC}" + ;; + esac +} + +install_lynx() { + printf "%b\n" "${YELLOW}Installing Lynx...${RC}" + case "$PACKAGER" in + apt-get|nala) + "$ESCALATION_TOOL" "$PACKAGER" install -y lynx + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER" install lynx + ;; + pacman) + "$ESCALATION_TOOL" "$PACKAGER" -S --noconfirm lynx + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install lynx + ;; + *) + printf "%b\n" "${RED}The script does not support your Distro. Install manually..${RC}" + ;; + esac +} + +browserSetup() { + clear + printf "%b\n" "Browser Installation Script" + printf "%b\n" "----------------------------" + printf "%b\n" "Select the browsers you want to install:" + printf "%b\n" "1. Google Chrome" + printf "%b\n" "2. Mozilla Firefox" + printf "%b\n" "3. Librewolf" + printf "%b\n" "4. Brave" + printf "%b\n" "5. Vivaldi" + printf "%b\n" "6. Chromium" + printf "%b\n" "7. Thorium" + printf "%b\n" "8. Lynx" + printf "%b\n" "----------------------------" + printf "%b\n" "Enter your choices (e.g., 1 3 5): " + read -r choice + for ch in $choice; do + case $ch in + 1) install_chrome ;; + 2) install_firefox ;; + 3) install_librewolf ;; + 4) install_brave ;; + 5) install_vivaldi ;; + 6) install_chromium ;; + 7) install_thorium ;; + 8) install_lynx;; + *) printf "%b\n" "${RED}Invalid option: $ch ${RC}" ;; + esac + done + printf "%b\n" "${GREEN}Installation complete!${RC}" +} + +checkEnv +checkEscalationTool +checkAURHelper +browserSetup diff --git a/tabs/applications-setup/dwmtitus-setup.sh b/tabs/applications-setup/dwmtitus-setup.sh index 598a0c63e..1632dd8f9 100755 --- a/tabs/applications-setup/dwmtitus-setup.sh +++ b/tabs/applications-setup/dwmtitus-setup.sh @@ -16,7 +16,7 @@ setupDWM() { "$ESCALATION_TOOL" "$PACKAGER" install -y libX11-devel libXinerama-devel libXft-devel imlib2-devel libxcb-devel ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" exit 1 ;; esac @@ -188,7 +188,7 @@ setupDisplayManager() { "$ESCALATION_TOOL" "$PACKAGER" install -y xorg-x11-xinit xorg-x11-server-Xorg ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" exit 1 ;; esac @@ -216,7 +216,7 @@ setupDisplayManager() { "$ESCALATION_TOOL" "$PACKAGER" install -y "$DM" ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" exit 1 ;; esac diff --git a/tabs/applications-setup/mybash-setup.sh b/tabs/applications-setup/mybash-setup.sh index 04772cf7f..d1facbb39 100644 --- a/tabs/applications-setup/mybash-setup.sh +++ b/tabs/applications-setup/mybash-setup.sh @@ -20,7 +20,7 @@ installDepend() { "$ESCALATION_TOOL" "$PACKAGER" install -y bash bash-completion tar bat tree unzip fontconfig git ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" # The packages above were grabbed out of the original mybash-setup-script. + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" # The packages above were grabbed out of the original mybash-setup-script. exit 1 ;; esac diff --git a/tabs/applications-setup/neovim-setup.sh b/tabs/applications-setup/neovim-setup.sh index 040587736..8bb451742 100755 --- a/tabs/applications-setup/neovim-setup.sh +++ b/tabs/applications-setup/neovim-setup.sh @@ -29,7 +29,7 @@ installNeovim() { "$ESCALATION_TOOL" "$PACKAGER" install -y neovim ripgrep fzf python3-virtualenv luarocks golang ShellCheck git ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" # The packages above were grabbed out of the original nvim-setup-script. + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" # The packages above were grabbed out of the original nvim-setup-script. exit 1 ;; esac diff --git a/tabs/applications-setup/setup-flatpak.sh b/tabs/applications-setup/setup-flatpak.sh new file mode 100644 index 000000000..5a1cf60f0 --- /dev/null +++ b/tabs/applications-setup/setup-flatpak.sh @@ -0,0 +1,91 @@ +#!/bin/sh -e + +. ../common-script.sh + +# Used to detect the desktop environment, Only used for the If statement in the setup_flatpak function. +# Perhaps this should be moved to common-script.sh later on? +detect_de() { + if [ -n "$XDG_CURRENT_DESKTOP" ]; then + case "$XDG_CURRENT_DESKTOP" in + *GNOME*) + DE="GNOME" + ;; + *KDE*) + DE="KDE" + ;; + esac + fi +} + +# Install Flatpak if not already installed. +setup_flatpak() { + if ! command_exists flatpak; then + printf "%b\n" "${YELLOW}Installing Flatpak...${RC}" + case "$PACKAGER" in + pacman) + "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm flatpak + ;; + apt-get|nala) + "$ESCALATION_TOOL" "$PACKAGER" install -y flatpak + ;; + dnf) + "$ESCALATION_TOOL" "$PACKAGER" install -y flatpak # Fedora should have flatpak already installed, this is just a failsafe + ;; + zypper) + "$ESCALATION_TOOL" "$PACKAGER" install -y flatpak + ;; + yum) + "$ESCALATION_TOOL" "$PACKAGER" install -y flatpak + ;; + xbps-install) + "$ESCALATION_TOOL" "$PACKAGER" install -S flatpak + ;; + *) + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" + exit 1 + ;; + esac + printf "%b\n" "Adding Flathub remote..." + "$ESCALATION_TOOL" flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + else + if command -v flatpak >/dev/null 2>&1; then + if ! flatpak remotes | grep -q "flathub"; then + printf "%b\n" "${YELLOW}Detected Flatpak package manager but Flathub remote is not added. Would you like to add it? (y/n)${RC}" + read add_remote + case "$add_remote" in + [Yy]*) + printf "%b\n" "Adding Flathub remote..." + "$ESCALATION_TOOL" flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + ;; + esac + else + # Needed mostly for systems without a polkit agent running (Error: updating: Unable to connect to system bus) + printf "%b\n" "${GREEN}Flathub already setup. You can quit.${RC}" + fi + fi + fi + + if [ "$PACKAGER" = "apt-get" ] || [ "$PACKAGER" = "nala" ]; then + detect_de + # Only used for Ubuntu GNOME. Ubuntu GNOME doesnt allow flathub to be added as a remote to their store. + # So in case the user wants to use a GUI siftware manager they can setup it here + if [ "$DE" = "GNOME" ]; then + printf "%b\n" "${YELLOW}Detected GNOME desktop environment. Would you like to install GNOME Software plugin for Flatpak? (y/n)${RC}" + read install_gnome + if [ "$install_gnome" = "y" ] || [ "$install_gnome" = "Y" ]; then + "$ESCALATION_TOOL" "$PACKAGER" install -y gnome-software-plugin-flatpak + fi + # Useful for Debian KDE spin as well + elif [ "$DE" = "KDE" ]; then + printf "%b\n" "${YELLOW}Detected KDE desktop environment. Would you like to install KDE Plasma Discover backend for Flatpak? (y/n)${RC}" + read install_kde + if [ "$install_kde" = "y" ] || [ "$install_kde" = "Y" ]; then + "$ESCALATION_TOOL" "$PACKAGER" install -y plasma-discover-backend-flatpak + fi + fi + fi +} + +checkEnv +checkEscalationTool +setup_flatpak diff --git a/tabs/applications-setup/tab_data.toml b/tabs/applications-setup/tab_data.toml index 0a9be9fbf..0a8633b1e 100644 --- a/tabs/applications-setup/tab_data.toml +++ b/tabs/applications-setup/tab_data.toml @@ -10,11 +10,30 @@ name = "Bash Prompt" description = "The .bashrc file is a script that runs every time a new terminal session is started in Unix-like operating systems.\nIt is used to configure the shell session, set up aliases, define functions, and more, making the terminal easier to use and more powerful.\nThis command configures the key sections and functionalities defined in the .bashrc file from CTT's mybash repository.\nhttps://github.com/ChrisTitusTech/mybash" script = "mybash-setup.sh" +[[data]] +name = "Bottles setup" +script = "bottles-setup.sh" + +[[data]] +name = "Browser Setup" +description = "An interactive script to install popular browsers." +script = "browser-setup.sh" + [[data]] name = "DWM-Titus" description = "DWM is a dynamic window manager for X.\nIt manages windows in tiled, monocle and floating layouts.\nAll of the layouts can be applied dynamically, optimising the environment for the application in use and the task performed.\nThis command installs and configures DWM and a desktop manager.\nThe list of patches applied can be found in CTT's DWM repository\nhttps://github.com/ChrisTitusTech/dwm-titus" script = "dwmtitus-setup.sh" +[[data]] +name = "Fastfetch" +description = "Fastfetch is a neofetch-like tool for fetching system information and displaying it prettily.\nIt is written mainly in C, with performance and customizability in mind.\nThis command installs fastfetch and configures from CTT's mybash repository.\nhttps://github.com/ChrisTitusTech/mybash" +script = "fastfetch-setup.sh" + +[[data]] +name = "Flatpak / Flathub" +description = "Flatpak is a universal application sandbox for Linux that uses isolated packages from Flathub to prevent conflicts and system alterations, while alleviating dependency concerns.\nThis command installs Flatpak and adds the Flathub repository" +script = "setup-flatpak.sh" + [[data]] name = "Kitty" description = "kitty is a free and open-source GPU-accelerated terminal emulator for Linux, macOS, and some BSD distributions, focused on performance and features.\nkitty is written in a mix of C and Python programming languages.\n This command installs and configures kitty." @@ -34,8 +53,3 @@ script = "rofi-setup.sh" name = "ZSH Prompt" description = "The Z shell is a Unix shell that can be used as an interactive login shell and as a command interpreter for shell scripting. Zsh is an extended Bourne shell with many improvements, including some features of Bash, ksh, and tcsh.\nThis command installs ZSH prompt and provides basic configuration." script = "zsh-setup.sh" - -[[data]] -name = "Fastfetch" -description = "Fastfetch is a neofetch-like tool for fetching system information and displaying it prettily.\nIt is written mainly in C, with performance and customizability in mind.\nThis command installs fastfetch and configures from CTT's mybash repository.\nhttps://github.com/ChrisTitusTech/mybash" -script = "fastfetch-setup.sh" diff --git a/tabs/common-script.sh b/tabs/common-script.sh index 769ae905a..c6003a841 100644 --- a/tabs/common-script.sh +++ b/tabs/common-script.sh @@ -9,7 +9,7 @@ CYAN='\033[36m' GREEN='\033[32m' command_exists() { - which "$1" >/dev/null 2>&1 + command -v "$1" >/dev/null 2>&1 } checkAURHelper() { @@ -20,7 +20,7 @@ checkAURHelper() { for helper in ${AUR_HELPERS}; do if command_exists "${helper}"; then AUR_HELPER=${helper} - printf "Using ${helper} as AUR helper" + printf "%b\n" "Using ${helper} as AUR helper" AUR_HELPER_CHECKED=true return 0 fi @@ -49,7 +49,7 @@ checkEscalationTool() { for tool in ${ESCALATION_TOOLS}; do if command_exists "${tool}"; then ESCALATION_TOOL=${tool} - printf "Using ${tool} for privilege escalation" + printf "%b\n" "Using ${tool} for privilege escalation" ESCALATION_TOOL_CHECKED=true return 0 fi @@ -77,7 +77,7 @@ checkPackageManager() { for pgm in ${PACKAGEMANAGER}; do if command_exists "${pgm}"; then PACKAGER=${pgm} - printf "Using ${pgm}" + printf "%b\n" "Using ${pgm}" break fi done @@ -94,7 +94,7 @@ checkSuperUser() { for sug in ${SUPERUSERGROUP}; do if groups | grep -q "${sug}"; then SUGROUP=${sug} - printf "Super user group ${SUGROUP}" + printf "%b\n" "Super user group ${SUGROUP}" break fi done diff --git a/tabs/gaming/diablo-ii/d2r-loot-filters.sh b/tabs/gaming/diablo-ii/d2r-loot-filters.sh index 9534108d1..57960eb2c 100755 --- a/tabs/gaming/diablo-ii/d2r-loot-filters.sh +++ b/tabs/gaming/diablo-ii/d2r-loot-filters.sh @@ -2,42 +2,62 @@ . ../../common-script.sh +# Check for required commands +for cmd in find curl unzip stty; do + if ! command -v "$cmd" >/dev/null 2>&1; then + printf "%s\n" "Error: $cmd is not installed." + exit 1 + fi +done + # Search for possible Diablo II Resurrected folder locations -printf "%b\n" "${YELLOW}Searching for Diablo II Resurrected folders...${RC}" -possible_paths=$(find $HOME -type d -path "*/drive_c/Program Files (x86)/Diablo II Resurrected" 2>/dev/null) +printf "%s\n" "Searching for Diablo II Resurrected folders..." +possible_paths=$(find "$HOME" -type d -path "*/drive_c/Program Files (x86)/Diablo II Resurrected" 2>/dev/null) if [ -z "$possible_paths" ]; then - printf "%b\n" "${RED}Error: No Diablo II Resurrected folders found.${RC}" + printf "%s\n" "Error: No Diablo II Resurrected folders found." exit 1 fi # Display possible paths and allow selection -printf "%b\n" "${YELLOW}Possible Diablo II Resurrected folder locations:${RC}" -mapfile -t paths_array <<< "$possible_paths" +printf "%s\n" "Possible Diablo II Resurrected folder locations:" +paths_string="" +i=0 +IFS=' +' +for path in $possible_paths; do + paths_string="$paths_string|$path" + i=$((i + 1)) +done +IFS=' ' +paths_string="${paths_string#|}" +total=$i selected=0 -total=${#paths_array[@]} print_menu() { - clear - local max_display=$((total < 10 ? total : 10)) - local start=$((selected - max_display/2)) - if ((start < 0)); then start=0; fi - if ((start + max_display > total)); then start=$((total - max_display)); fi - if ((start < 0)); then start=0; fi + command -v clear >/dev/null 2>&1 && clear + max_display=$((total < 10 ? total : 10)) + start=$((selected - max_display / 2)) + if [ $start -lt 0 ]; then start=0; fi + if [ $((start + max_display)) -gt $total ]; then start=$((total - max_display)); fi + if [ $start -lt 0 ]; then start=0; fi - printf "%b\n" "${YELLOW}Please select the Diablo II: Resurrected installation path:${RC}" - for i in $(seq 0 $((max_display - 1))); do - if ((i + start >= total)); then break; fi - if [ $((i + start)) -eq $selected ]; then - echo "> ${paths_array[$((i + start))]}" - else - echo " ${paths_array[$((i + start))]}" + printf "%s\n" "Please select the Diablo II: Resurrected installation path:" + i=0 + echo "$paths_string" | tr '|' '\n' | while IFS= read -r path; do + if [ $i -ge $start ] && [ $i -lt $((start + max_display)) ]; then + if [ $i -eq $selected ]; then + printf "> %s\n" "$path" + else + printf " %s\n" "$path" + fi fi + i=$((i + 1)) done } select_path() { - local last_selected=-1 + last_selected=-1 while true; do if [ $last_selected -ne $selected ]; then @@ -45,31 +65,29 @@ select_path() { last_selected=$selected fi - read -rsn1 key + stty -echo + key=$(dd bs=1 count=1 2>/dev/null) + stty echo + case "$key" in - $'\x1B') # ESC key - read -rsn2 key - case "$key" in - '[A' | 'k') - if ((selected > 0)); then - ((selected--)) - fi - ;; - '[B' | 'j') - if ((selected < total - 1)); then - ((selected++)) - fi - ;; - esac + $(printf '\033')A | k) + if [ $selected -gt 0 ]; then + selected=$((selected - 1)) + fi ;; - '') # Enter key - d2r_path="${paths_array[$selected]}" + $(printf '\033')B | j) + if [ $selected -lt $((total - 1)) ]; then + selected=$((selected + 1)) + fi + ;; + '') + d2r_path=$(echo "$paths_string" | cut -d '|' -f $((selected + 1))) break ;; esac done - clear # Clear the screen after selection + command -v clear >/dev/null 2>&1 && clear } # Use the select_path function @@ -77,7 +95,7 @@ select_path # Validate the path if [ ! -d "$d2r_path" ]; then - printf "%b\n" "${RED}Error: The specified path does not exist.${RC}" + printf "%s\n" "Error: The specified path does not exist." exit 1 fi @@ -86,17 +104,23 @@ mods_path="$d2r_path/mods" mkdir -p "$mods_path" # Download the latest release -printf "%b\n" "${YELLOW}Downloading the latest loot filter...${RC}" -curl -sSLo /tmp/lootfilter.zip https://github.com/ChrisTitusTech/d2r-loot-filter/releases/latest/download/lootfilter.zip +printf "%s\n" "Downloading the latest loot filter..." +if ! curl -sSLo /tmp/lootfilter.zip https://github.com/ChrisTitusTech/d2r-loot-filter/releases/latest/download/lootfilter.zip; then + printf "%s\n" "Error: Failed to download the loot filter." + exit 1 +fi # Extract the contents to the mods folder -printf "%b\n" "${YELLOW}Extracting loot filter to $mods_path...${RC}" -unzip -q -o /tmp/lootfilter.zip -d "$mods_path" +printf "%s\n" "Extracting loot filter to $mods_path..." +if ! unzip -q -o /tmp/lootfilter.zip -d "$mods_path"; then + printf "%s\n" "Error: Failed to extract the loot filter." + exit 1 +fi # Clean up rm /tmp/lootfilter.zip -printf "%b\n" "${GREEN}Loot filter installed successfully in $mods_path${RC}" +printf "%s\n" "Loot filter installed successfully in $mods_path" printf "\nTo complete the setup, please follow these steps to add launch options in Battle.net:\n" printf "1. Open the Battle.net launcher\n" diff --git a/tabs/system-setup/1-compile-setup.sh b/tabs/system-setup/1-compile-setup.sh index 94ccf7da1..81c629ec8 100755 --- a/tabs/system-setup/1-compile-setup.sh +++ b/tabs/system-setup/1-compile-setup.sh @@ -43,28 +43,7 @@ installDepend() { esac } -install_additional_dependencies() { - case $(command -v apt || command -v zypper || command -v dnf || command -v pacman) in - *apt) - # Add additional dependencies for apt if needed - ;; - *zypper) - # Add additional dependencies for zypper if needed - ;; - *dnf) - # Add additional dependencies for dnf if needed - ;; - *pacman) - # Add additional dependencies for pacman if needed - ;; - *) - # Add additional dependencies for other package managers if needed - ;; - esac -} - checkEnv checkAURHelper checkEscalationTool -installDepend -install_additional_dependencies \ No newline at end of file +installDepend \ No newline at end of file diff --git a/tabs/system-setup/5-samba-ssh-setup.sh b/tabs/system-setup/5-samba-ssh-setup.sh index 7e22f2624..b130b290a 100755 --- a/tabs/system-setup/5-samba-ssh-setup.sh +++ b/tabs/system-setup/5-samba-ssh-setup.sh @@ -9,10 +9,10 @@ install_package() { if ! command_exists "$PACKAGE"; then case "$PACKAGER" in pacman) - $ESCALATION_TOOL "$PACKAGER" -S --noconfirm "$PACKAGE" + "$ESCALATION_TOOL" "$PACKAGER" -S --noconfirm "$PACKAGE" ;; *) - $ESCALATION_TOOL "$PACKAGER" install -y "$PACKAGE" + "$ESCALATION_TOOL" "$PACKAGER" install -y "$PACKAGE" ;; esac else @@ -41,8 +41,8 @@ setup_ssh() { esac # Enable and start the appropriate SSH service - $ESCALATION_TOOL systemctl enable "$SSH_SERVICE" - $ESCALATION_TOOL systemctl start "$SSH_SERVICE" + "$ESCALATION_TOOL" systemctl enable "$SSH_SERVICE" + "$ESCALATION_TOOL" systemctl start "$SSH_SERVICE" # Get the local IP address LOCAL_IP=$(ip -4 addr show | awk '/inet / {print $2}' | tail -n 1) @@ -82,8 +82,8 @@ setup_samba() { SHARED_DIR=${SHARED_DIR:-/srv/samba/share} # Create the shared directory if it doesn't exist - $ESCALATION_TOOL mkdir -p "$SHARED_DIR" - $ESCALATION_TOOL chmod -R 0777 "$SHARED_DIR" + "$ESCALATION_TOOL" mkdir -p "$SHARED_DIR" + "$ESCALATION_TOOL" chmod -R 0777 "$SHARED_DIR" # Add a new Samba user printf "Enter Samba username: " @@ -109,10 +109,10 @@ setup_samba() { done # Add the user and set the password - $ESCALATION_TOOL smbpasswd -a "$SAMBA_USER" + "$ESCALATION_TOOL" smbpasswd -a "$SAMBA_USER" # Configure Samba settings - $ESCALATION_TOOL sh -c "cat > $SAMBA_CONFIG" < $SAMBA_CONFIG" < /dev/null + printf "%b\n" "$fstab_entry" | "$ESCALATION_TOOL" tee -a /etc/fstab > /dev/null + printf "%b\n" "" | "$ESCALATION_TOOL" tee -a /etc/fstab > /dev/null + + printf "%b\n" "Entry added to /etc/fstab:" + printf "%b\n" "$comment" + printf "%b\n" "$fstab_entry" +} + + +# Function to mount the drive +mount_drive() { + printf "%b\n" "Mounting the drive..." + "$ESCALATION_TOOL" mount -a + if mount | grep "$mount_point" > /dev/null; then + printf "%b\n" "${GREEN}Drive mounted successfully at $mount_point${RC}." + else + printf "%b\n" "${RED}Failed to mount the drive.${RC}" + exit 1 + fi +} + +checkEnv +checkEscalationTool +select_drive +get_uuid_fstype +create_mount_point +update_fstab +mount_drive diff --git a/tabs/system-setup/arch/paru-setup.sh b/tabs/system-setup/arch/paru-setup.sh index 05b78c80c..d383764fd 100755 --- a/tabs/system-setup/arch/paru-setup.sh +++ b/tabs/system-setup/arch/paru-setup.sh @@ -7,8 +7,8 @@ installDepend() { pacman) if ! command_exists paru; then printf "%b\n" "${YELLOW}Installing paru as AUR helper...${RC}" - $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel - cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/paru.git && $ESCALATION_TOOL chown -R "$USER": ./paru + "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm base-devel + cd /opt && "$ESCALATION_TOOL" git clone https://aur.archlinux.org/paru.git && "$ESCALATION_TOOL" chown -R "$USER": ./paru cd paru && makepkg --noconfirm -si printf "%b\n" "${GREEN}Paru installed${RC}" else @@ -16,7 +16,7 @@ installDepend() { fi ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" ;; esac } diff --git a/tabs/system-setup/arch/yay-setup.sh b/tabs/system-setup/arch/yay-setup.sh index f7f9fa6aa..398f87003 100755 --- a/tabs/system-setup/arch/yay-setup.sh +++ b/tabs/system-setup/arch/yay-setup.sh @@ -7,8 +7,8 @@ installDepend() { pacman) if ! command_exists yay; then printf "%b\n" "${YELLOW}Installing yay as AUR helper...${RC}" - $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel - cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/yay-bin.git && $ESCALATION_TOOL chown -R "$USER": ./yay-bin + "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm base-devel + cd /opt && "$ESCALATION_TOOL" git clone https://aur.archlinux.org/yay-bin.git && "$ESCALATION_TOOL" chown -R "$USER": ./yay-bin cd yay-bin && makepkg --noconfirm -si printf "%b\n" "${GREEN}Yay installed${RC}" else @@ -16,7 +16,7 @@ installDepend() { fi ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" ;; esac } diff --git a/tabs/system-setup/fedora/configure-dnf.sh b/tabs/system-setup/fedora/configure-dnf.sh index af460545a..b51c8b4cc 100644 --- a/tabs/system-setup/fedora/configure-dnf.sh +++ b/tabs/system-setup/fedora/configure-dnf.sh @@ -3,13 +3,13 @@ . ../../common-script.sh configureDNF() { - case $PACKAGER in + case "$PACKAGER" in dnf) - printf "%b\n" ${YELLOW}"Configuring DNF...${RC}" - $ESCALATION_TOOL sed -i '/^max_parallel_downloads=/c\max_parallel_downloads=10' /etc/dnf/dnf.conf || echo 'max_parallel_downloads=10' >> /etc/dnf/dnf.conf - echo "fastestmirror=True" | $ESCALATION_TOOL tee -a /etc/dnf/dnf.conf > /dev/null - echo "defaultyes=True" | $ESCALATION_TOOL tee -a /etc/dnf/dnf.conf > /dev/null - $ESCALATION_TOOL "$PACKAGER" -y install dnf-plugins-core + printf "%b\n" "${YELLOW}Configuring DNF...${RC}" + "$ESCALATION_TOOL" sed -i '/^max_parallel_downloads=/c\max_parallel_downloads=10' /etc/dnf/dnf.conf || echo 'max_parallel_downloads=10' >> /etc/dnf/dnf.conf + echo "fastestmirror=True" | "$ESCALATION_TOOL" tee -a /etc/dnf/dnf.conf > /dev/null + echo "defaultyes=True" | "$ESCALATION_TOOL" tee -a /etc/dnf/dnf.conf > /dev/null + "$ESCALATION_TOOL" "$PACKAGER" -y install dnf-plugins-core printf "%b\n" "${GREEN}DNF Configured Succesfully...${RC}" ;; *) diff --git a/tabs/system-setup/fedora/multimedia-codecs.sh b/tabs/system-setup/fedora/multimedia-codecs.sh index a5db96a79..751e4547b 100644 --- a/tabs/system-setup/fedora/multimedia-codecs.sh +++ b/tabs/system-setup/fedora/multimedia-codecs.sh @@ -3,13 +3,13 @@ . ../../common-script.sh multimedia() { - case $PACKAGER in + case "$PACKAGER" in dnf) if [ -e /etc/yum.repos.d/rpmfusion-free.repo ] && [ -e /etc/yum.repos.d/rpmfusion-nonfree.repo ]; then printf "%b\n" "${YELLOW}Installing Multimedia Codecs...${RC}" - $ESCALATION_TOOL "$PACKAGER" swap ffmpeg-free ffmpeg --allowerasing -y - $ESCALATION_TOOL "$PACKAGER" update @multimedia --setopt="install_weak_deps=False" --exclude=PackageKit-gstreamer-plugin -y - $ESCALATION_TOOL "$PACKAGER" update @sound-and-video -y + "$ESCALATION_TOOL" "$PACKAGER" swap ffmpeg-free ffmpeg --allowerasing -y + "$ESCALATION_TOOL" "$PACKAGER" update @multimedia --setopt="install_weak_deps=False" --exclude=PackageKit-gstreamer-plugin -y + "$ESCALATION_TOOL" "$PACKAGER" update @sound-and-video -y printf "%b\n" "${GREEN}Multimedia Codecs Installed...${RC}" else printf "%b\n" "${RED}RPM Fusion repositories not found. Please set up RPM Fusion first!${RC}" diff --git a/tabs/system-setup/fedora/nvidia-proprietary-driver-setup.sh b/tabs/system-setup/fedora/nvidia-proprietary-driver-setup.sh new file mode 100755 index 000000000..7abfba7ca --- /dev/null +++ b/tabs/system-setup/fedora/nvidia-proprietary-driver-setup.sh @@ -0,0 +1,94 @@ +#!/bin/sh -e + +. ../../common-script.sh +# This script allows user to download proprietary drivers for nvidia in fedora + +# It also disables noveau nvidia drivers + +# Installation guide link: https://rpmfusion.org/Howto/NVIDIA + +# NOTE: Currently script only provides drivers for gpu 2014 and above (510+ and above) + +checkRepo() { + REPO_ID="rpmfusion-nonfree-nvidia-driver" + + if [ $(dnf repolist enabled 2>/dev/null | grep "$REPO_ID" | wc -l) -gt 0 ]; then + printf "%b\n" "${GREEN}Nvidia non-free repository is already enabled.${RC}" + else + printf "%b\n" "${YELLOW}Nvidia non-free repository is not enabled. Enabling now...${RC}" + + # Enable the repository + "$ESCALATION_TOOL" dnf config-manager --set-enabled "$REPO_ID" + + # Refreshing repository list + "$ESCALATION_TOOL" dnf makecache + + # Verify if the repository is enabled + if [ $(dnf repolist enabled 2>/dev/null | grep "$REPO_ID" | wc -l) -gt 0 ]; then + printf "%b\n" "${GREEN}Nvidia non-free repository is now enabled...${RC}" + else + printf "%b\n" "${RED}Failed to enable nvidia non-free repository...${RC}" + exit 1 + fi + fi +} + +checkDriverInstallation() { + if modinfo -F version nvidia >/dev/null 2>&1; then + return 0 + else + return 1 + fi +} + +installDriver() { + + if checkDriverInstallation; then + printf "%b\n" "${GREEN}NVIDIA driver is already installed.${RC}" + exit 0 + fi + + # NOTE:: Installing graphics driver. + "$ESCALATION_TOOL" dnf install akmod-nvidia xorg-x11-drv-nvidia-cuda -y + printf "%b\n" "${YELLOW}Building the drivers may take upto 5 minutes. Please don't kill the script!\n If the build failed try running the script again, select \"Remove Nvidia Drivers\" and reboot the system, then try installing drivers again.${RC}" + + for i in $(seq 1 5); do + if checkDriverInstallation; then + printf "%b\n" "${GREEN}Driver installed successfully.${RC}" + printf "%b\n" "${GREEN}Installed driver version $(modinfo -F version nvidia)${RC}" + break + fi + printf "%b\n" "${YELLOW}Waiting for driver to be built..." + sleep 1m + done + + printf "%b\n" "${GREEN}Now you can reboot the system.${RC}" + +} + +# NOTE: A confirmation option to proceed or not +userConfirmation() { + printf "%b" "${YELLOW}Do you want to continue? (Y/N): ${RC}" + read -r choice + case "$choice" in + y | Y) + checkRepo + installDriver + return + ;; + n | N) + printf "%b\n" "${RED} Exiting the Script ${RC}" + return + ;; + *) + printf "%b\n" "${RED} Invalid Option! ${RC}" + userConfirmation + ;; + esac +} + +printf "%b\n" "${YELLOW}Warning! This script will enable Nvidia non-free repository and only install drivers for GPUs from 2014 or later. It works on fedora 34 and above.\n It is recommended remove this driver while updating your kernel packages to newer version.${RC}" + +checkEnv +checkEscalationTool +userConfirmation \ No newline at end of file diff --git a/tabs/system-setup/fedora/virtualization.sh b/tabs/system-setup/fedora/virtualization.sh new file mode 100644 index 000000000..626c2e43e --- /dev/null +++ b/tabs/system-setup/fedora/virtualization.sh @@ -0,0 +1,21 @@ +#!/bin/sh -e + +. ../../common-script.sh + +# Install virtualization tools to enable virtual machines +configureVirtualization() { + case "$PACKAGER" in + dnf) + printf "%b\n" ${YELLOW}"Installing virtualization tools...${RC}" + "$ESCALATION_TOOL" "$PACKAGER" install -y @virtualization + printf "%b\n" "${GREEN}Installed virtualization tools...${RC}" + ;; + *) + printf "%b\n" "${RED}Unsupported distribution: $DTYPE${RC}" + ;; + esac +} + +checkEnv +checkEscalationTool +configureVirtualization diff --git a/tabs/system-setup/system-cleanup.sh b/tabs/system-setup/system-cleanup.sh index 625bc5bf4..40d5be2a4 100644 --- a/tabs/system-setup/system-cleanup.sh +++ b/tabs/system-setup/system-cleanup.sh @@ -26,7 +26,7 @@ cleanup_system() { "$ESCALATION_TOOL" "$PACKAGER" -Rns "$(pacman -Qtdq)" --noconfirm ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" return 1 ;; esac diff --git a/tabs/system-setup/system-update.sh b/tabs/system-setup/system-update.sh index c672a948e..521fd124d 100755 --- a/tabs/system-setup/system-update.sh +++ b/tabs/system-setup/system-update.sh @@ -6,7 +6,9 @@ fastUpdate() { case "$PACKAGER" in pacman) - $AUR_HELPER -S --needed --noconfirm rate-mirrors-bin + $AUR_HELPER -S --needed --noconfirm rate-mirrors-bin + + printf "%b\n" "${YELLOW}Generating a new list of mirrors using rate-mirrors. This process may take a few seconds...${RC}" if [ -s /etc/pacman.d/mirrorlist ]; then "$ESCALATION_TOOL" cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak @@ -54,7 +56,7 @@ fastUpdate() { "$ESCALATION_TOOL" "$PACKAGER" -Syu- ;; *) - printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" exit 1 ;; esac @@ -83,7 +85,7 @@ updateSystem() { "$ESCALATION_TOOL" "$PACKAGER" -Syu ;; *) - printf "%b\n" "${RED}Unsupported package manager: ${PACKAGER}${RC}" + printf "%b\n" "${RED}Unsupported package manager: "$PACKAGER"${RC}" exit 1 ;; esac @@ -91,7 +93,18 @@ updateSystem() { updateFlatpaks() { if command_exists flatpak; then - flatpak update -y + printf "%b\n" "${YELLOW}Updating installed Flathub apps...${RC}" + installed_apps=$(flatpak list --app --columns=application) + + if [ -z "$installed_apps" ]; then + printf "%b\n" "${RED}No Flathub apps are installed.${RC}" + return + fi + + for app in $installed_apps; do + printf "%b\n" "${YELLOW}Updating $app...${RC}" + flatpak update -y "$app" + done fi } @@ -100,4 +113,4 @@ checkAURHelper checkEscalationTool fastUpdate updateSystem -updateFlatpaks \ No newline at end of file +updateFlatpaks diff --git a/tabs/system-setup/tab_data.toml b/tabs/system-setup/tab_data.toml index 178a5680e..96a76664b 100644 --- a/tabs/system-setup/tab_data.toml +++ b/tabs/system-setup/tab_data.toml @@ -29,21 +29,44 @@ name = "Fedora" [[data.preconditions]] matches = true data = "command_exists" -values = ["dnf"] - -[[data.entries]] -name = "RPM Fusion Setup" -description = "RPM Fusion provides software that the Fedora Project or Red Hat doesn't want to ship.\nThat software is provided as precompiled RPMs for all current Fedora versions and current Red Hat Enterprise Linux or clones versions; you can use the RPM Fusion repositories with tools like yum and PackageKit.\nFor more information visit: https://rpmfusion.org/" -script = "fedora/rpm-fusion-setup.sh" +values = ["dnf"] [[data.entries]] name = "Configure DNF" +description = "Optimizes DNF for parallel downloads" script = "fedora/configure-dnf.sh" [[data.entries]] name = "Multimedia Setup" script = "fedora/multimedia-codecs.sh" +[[data.entries]] +name = "Nvidia Proprietary Driver Setup" +script = "fedora/nvidia-proprietary-driver-setup.sh" + +[[data.entries]] +name = "RPM Fusion Setup" +description = "RPM Fusion provides software that the Fedora Project or Red Hat doesn't want to ship.\nThat software is provided as precompiled RPMs for all current Fedora versions and current Red Hat Enterprise Linux or clones versions; you can use the RPM Fusion repositories with tools like yum and PackageKit.\nFor more information visit: https://rpmfusion.org/" +script = "fedora/rpm-fusion-setup.sh" + +[[data.entries]] +name = "Virtualization Setup" +description = "Enables Virtualization through dnf" +script = "fedora/virtualization.sh" + +[[data]] +name = "Auto Mount Drive" +script = "9-auto-mount.sh" + +[[data]] +name = "Build Prerequisites" +description = "This script is designed to handle the installation of various software dependencies across different Linux distributions" +script = "1-compile-setup.sh" + +[[data]] +name = "Docker Setup" +script = "6-docker-setup.sh" + [[data]] name = "Full System Cleanup" script = "system-cleanup.sh" @@ -53,11 +76,6 @@ name = "Full System Update" description = "This command updates your system to the latest packages available for your distro" script = "system-update.sh" -[[data]] -name = "Build Prerequisites" -description = "This script is designed to handle the installation of various software dependencies across different Linux distributions" -script = "1-compile-setup.sh" - [[data]] name = "Gaming Dependencies" description = "This script is designed to handle the installation of gaming dependencies across different Linux distributions" @@ -68,6 +86,10 @@ name = "Global Theme" description = "This script is designed to handle the installation and configuration of global theming" script = "3-global-theme.sh" +[[data]] +name = "Grub Theme Setup" +script = "7-grub-theme.sh" + [[data]] name = "Remove Snaps" description = "This script is designed to remove snap" @@ -76,7 +98,3 @@ script = "4-remove-snaps.sh" [[data]] name = "SSH-Samba Setup" script = "5-samba-ssh-setup.sh" - -[[data]] -name = "Docker Setup" -script = "6-docker-setup.sh" \ No newline at end of file diff --git a/tabs/utils/auto-login.sh b/tabs/utils/auto-login.sh index aa73211c5..b5ce0131b 100644 --- a/tabs/utils/auto-login.sh +++ b/tabs/utils/auto-login.sh @@ -44,9 +44,9 @@ configure_lightdm() { printf "Enter username for LightDM autologin: " read -r user - $ESCALATION_TOOL "printf '[Seat:*]' > /etc/lightdm/lightdm.conf.d/50-autologin.conf" - $ESCALATION_TOOL "printf 'autologin-user=$user' >> /etc/lightdm/lightdm.conf.d/50-autologin.conf" - $ESCALATION_TOOL "printf 'autologin-user-timeout=0' >> /etc/lightdm/lightdm.conf.d/50-autologin.conf" + "$ESCALATION_TOOL" "printf '[Seat:*]' > /etc/lightdm/lightdm.conf.d/50-autologin.conf" + "$ESCALATION_TOOL" "printf 'autologin-user=$user' >> /etc/lightdm/lightdm.conf.d/50-autologin.conf" + "$ESCALATION_TOOL" "printf 'autologin-user-timeout=0' >> /etc/lightdm/lightdm.conf.d/50-autologin.conf" printf "LightDM has been configured for autologin.\n" } @@ -54,7 +54,7 @@ configure_lightdm() { # Function to remove LightDM autologin remove_lightdm_autologin() { printf "Removing LightDM autologin configuration...\n" - $ESCALATION_TOOL rm -f /etc/lightdm/lightdm.conf.d/50-autologin.conf + "$ESCALATION_TOOL" rm -f /etc/lightdm/lightdm.conf.d/50-autologin.conf printf "LightDM autologin configuration has been removed.\n" } @@ -65,9 +65,9 @@ configure_gdm() { printf "Enter username for GDM autologin: " read -r user - $ESCALATION_TOOL "printf '[daemon]' > /etc/gdm/custom.conf" - $ESCALATION_TOOL "printf 'AutomaticLoginEnable = true' >> /etc/gdm/custom.conf" - $ESCALATION_TOOL "printf 'AutomaticLogin = $user' >> /etc/gdm/custom.conf" + "$ESCALATION_TOOL" "printf '[daemon]' > /etc/gdm/custom.conf" + "$ESCALATION_TOOL" "printf 'AutomaticLoginEnable = true' >> /etc/gdm/custom.conf" + "$ESCALATION_TOOL" "printf 'AutomaticLogin = $user' >> /etc/gdm/custom.conf" printf "GDM has been configured for autologin.\n" } @@ -75,8 +75,8 @@ configure_gdm() { # Function to remove GDM autologin remove_gdm_autologin() { printf "Removing GDM autologin configuration...\n" - $ESCALATION_TOOL sed -i '/AutomaticLoginEnable/d' /etc/gdm/custom.conf - $ESCALATION_TOOL sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf + "$ESCALATION_TOOL" sed -i '/AutomaticLoginEnable/d' /etc/gdm/custom.conf + "$ESCALATION_TOOL" sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf printf "GDM autologin configuration has been removed.\n" } @@ -88,9 +88,9 @@ configure_sddm() { read -r user list_sessions # Show session options - $ESCALATION_TOOL "printf '[Autologin]' > /etc/sddm.conf" - $ESCALATION_TOOL "printf 'User=$user' >> /etc/sddm.conf" - $ESCALATION_TOOL "printf 'Session=$session' >> /etc/sddm.conf" + "$ESCALATION_TOOL" "printf '[Autologin]' > /etc/sddm.conf" + "$ESCALATION_TOOL" "printf 'User=$user' >> /etc/sddm.conf" + "$ESCALATION_TOOL" "printf 'Session=$session' >> /etc/sddm.conf" printf "SDDM has been configured for autologin.\n" } @@ -98,7 +98,7 @@ configure_sddm() { # Function to remove SDDM autologin remove_sddm_autologin() { printf "Removing SDDM autologin configuration...\n" - $ESCALATION_TOOL sed -i '/\[Autologin\]/,+2d' /etc/sddm.conf + "$ESCALATION_TOOL" sed -i '/\[Autologin\]/,+2d' /etc/sddm.conf printf "SDDM autologin configuration has been removed.\n" } @@ -110,8 +110,8 @@ configure_lxdm() { read -r user list_sessions # Show session options - $ESCALATION_TOOL sed -i "s/^#.*autologin=.*$/autologin=${user}/" /etc/lxdm/lxdm.conf - $ESCALATION_TOOL sed -i "s|^#.*session=.*$|session=/usr/bin/${session}|; s|^session=.*$|session=/usr/bin/${session}|" /etc/lxdm/lxdm.conf + "$ESCALATION_TOOL" sed -i "s/^#.*autologin=.*$/autologin=${user}/" /etc/lxdm/lxdm.conf + "$ESCALATION_TOOL" sed -i "s|^#.*session=.*$|session=/usr/bin/${session}|; s|^session=.*$|session=/usr/bin/${session}|" /etc/lxdm/lxdm.conf printf "LXDM has been configured for autologin.\n" } @@ -119,8 +119,8 @@ configure_lxdm() { # Function to remove LXDM autologin remove_lxdm_autologin() { printf "Removing LXDM autologin configuration...\n" - $ESCALATION_TOOL sed -i "s/^autologin=.*$/#autologin=/" /etc/lxdm/lxdm.conf - $ESCALATION_TOOL sed -i "s/^session=.*$/#session=/" /etc/lxdm/lxdm.conf + "$ESCALATION_TOOL" sed -i "s/^autologin=.*$/#autologin=/" /etc/lxdm/lxdm.conf + "$ESCALATION_TOOL" sed -i "s/^session=.*$/#session=/" /etc/lxdm/lxdm.conf printf "LXDM autologin configuration has been removed.\n" } diff --git a/tabs/utils/create-bootable-usb.sh b/tabs/utils/create-bootable-usb.sh index e96c73a93..17e422c24 100644 --- a/tabs/utils/create-bootable-usb.sh +++ b/tabs/utils/create-bootable-usb.sh @@ -13,7 +13,7 @@ usage() { list_devices() { printf "%b\n" "${YELLOW} Available devices and partitions: ${RC}" printf "\n" - $ESCALATION_TOOL lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL + "$ESCALATION_TOOL" lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL printf "\n" } @@ -164,13 +164,13 @@ write_iso(){ # Display progress and create the bootable USB drive printf "%b\n" "${YELLOW}Creating bootable USB drive...${RC}" - if ! $ESCALATION_TOOL dd if="$ISO_PATH" of="$USB_DEVICE" bs=4M status=progress oflag=sync; then + if ! "$ESCALATION_TOOL" dd if="$ISO_PATH" of="$USB_DEVICE" bs=4M status=progress oflag=sync; then printf "%b\n" "${RED}Failed to create bootable USB drive${RC}" exit 1 fi # Sync to ensure all data is written - if ! $ESCALATION_TOOL sync; then + if ! "$ESCALATION_TOOL" sync; then printf "%b\n" "${RED}Failed to sync data${RC}" exit 1 fi @@ -179,10 +179,10 @@ write_iso(){ # Eject the USB device printf "%b\n" "${YELLOW}Ejecting ${USB_DEVICE}...${RC}" - if ! $ESCALATION_TOOL umount "${USB_DEVICE}"* 2>/dev/null; then + if ! "$ESCALATION_TOOL" umount "${USB_DEVICE}"* 2>/dev/null; then printf "%b\n" "${RED}Failed to unmount ${USB_DEVICE}${RC}" fi - if ! $ESCALATION_TOOL eject "$USB_DEVICE"; then + if ! "$ESCALATION_TOOL" eject "$USB_DEVICE"; then printf "%b\n" "${RED}Failed to eject ${USB_DEVICE}${RC}" fi diff --git a/tabs/utils/encrypt_decrypt_tool.sh b/tabs/utils/encrypt_decrypt_tool.sh index 48945b717..8a4f033f4 100644 --- a/tabs/utils/encrypt_decrypt_tool.sh +++ b/tabs/utils/encrypt_decrypt_tool.sh @@ -6,18 +6,18 @@ printf "%b\n" "${YELLOW}Ensuring OpenSSL is installed...${RC}" # Install OpenSSL if ! command_exists openssl; then - case $PACKAGER in + case "$PACKAGER" in pacman) - $ESCALATION_TOOL ${PACKAGER} -Syu --noconfirm openssl + "$ESCALATION_TOOL" "$PACKAGER" -Syu --noconfirm openssl ;; apt-get) - $ESCALATION_TOOL ${PACKAGER} update && $ESCALATION_TOOL ${PACKAGER} install -y openssl + "$ESCALATION_TOOL" "$PACKAGER" update && "$ESCALATION_TOOL" "$PACKAGER" install -y openssl ;; dnf) - $ESCALATION_TOOL ${PACKAGER} install -y openssl + "$ESCALATION_TOOL" "$PACKAGER" install -y openssl ;; zypper) - $ESCALATION_TOOL ${PACKAGER} install openssl + "$ESCALATION_TOOL" "$PACKAGER" install openssl ;; *) printf "%b\n" "${RED}Your Linux distribution is not supported by this script.${RC}" @@ -54,8 +54,8 @@ encrypt_file() { echo "Enter the path for the encrypted file or directory:" read -r OUTPUT_PATH - echo "Enter the encryption password:" - read -s -r PASSWORD + printf "Enter the encryption password: " + read -r PASSWORD if [ -d "$INPUT_PATH" ]; then # Encrypt each file in the directory @@ -99,8 +99,8 @@ decrypt_file() { echo "Enter the path for the decrypted file or directory:" read -r OUTPUT_PATH - echo "Enter the decryption password:" - read -s -r PASSWORD + printf "Enter the decryption password: " + read -r PASSWORD if [ -d "$INPUT_PATH" ]; then # Decrypt each file in the directory @@ -145,8 +145,8 @@ main(){ *) echo "Invalid choice. Please try again." ;; esac - echo "Press [Enter] to continue..." - read -r + printf "Press [Enter] to continue..." + read -r dummy done } diff --git a/tabs/utils/power-profile.sh b/tabs/utils/power-profile.sh index 3cc1823fc..4ab801331 100644 --- a/tabs/utils/power-profile.sh +++ b/tabs/utils/power-profile.sh @@ -15,12 +15,12 @@ installAutoCpufreq() { # Install git if not already installed if ! command_exists git; then printf "%b\n" "${YELLOW}git not found. Installing git...${RC}" - case ${PACKAGER} in + case "$PACKAGER" in pacman) - $ESCALATION_TOOL ${PACKAGER} -S --needed --noconfirm git + "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm git ;; *) - $ESCALATION_TOOL ${PACKAGER} install -y git + "$ESCALATION_TOOL" "$PACKAGER" install -y git ;; esac fi @@ -31,11 +31,11 @@ installAutoCpufreq() { git clone https://github.com/AdnanHodzic/auto-cpufreq.git fi - case ${PACKAGER} in + case "$PACKAGER" in *) cd auto-cpufreq printf "%b\n" "${YELLOW}Running auto-cpufreq installer...${RC}" - $ESCALATION_TOOL ./auto-cpufreq-installer + "$ESCALATION_TOOL" ./auto-cpufreq-installer ;; esac cd .. @@ -49,10 +49,10 @@ configureAutoCpufreq() { # Check if the system has a battery to determine if it's a laptop if [ -d /sys/class/power_supply/BAT0 ]; then printf "%b\n" "${GREEN}System detected as laptop. Updating auto-cpufreq for laptop...${RC}" - $ESCALATION_TOOL auto-cpufreq --force powersave + "$ESCALATION_TOOL" auto-cpufreq --force powersave else printf "%b\n" "${GREEN}System detected as desktop. Updating auto-cpufreq for desktop...${RC}" - $ESCALATION_TOOL auto-cpufreq --force performance + "$ESCALATION_TOOL" auto-cpufreq --force performance fi else printf "%b\n" "${RED}auto-cpufreq is not installed, skipping configuration.${RC}" @@ -64,7 +64,7 @@ removeAutoCpufreqTweak() { if command_exists auto-cpufreq; then printf "%b\n" "${YELLOW}Resetting auto-cpufreq configuration...${RC}" - $ESCALATION_TOOL auto-cpufreq --force reset + "$ESCALATION_TOOL" auto-cpufreq --force reset else printf "%b\n" "${RED}auto-cpufreq is not installed, skipping removal.${RC}" fi diff --git a/tabs/utils/service-control.sh b/tabs/utils/service-control.sh index 119e45f31..4539f4448 100644 --- a/tabs/utils/service-control.sh +++ b/tabs/utils/service-control.sh @@ -92,11 +92,11 @@ add_service() { printf "\n" printf "[Install]\n" printf "WantedBy=multi-user.target\n" - } | $ESCALATION_TOOL tee "$SERVICE_FILE" > /dev/null + } | "$ESCALATION_TOOL" tee "$SERVICE_FILE" > /dev/null # Set permissions and reload systemd - $ESCALATION_TOOL chmod 644 "$SERVICE_FILE" - $ESCALATION_TOOL systemctl daemon-reload + "$ESCALATION_TOOL" chmod 644 "$SERVICE_FILE" + "$ESCALATION_TOOL" systemctl daemon-reload printf "Service $SERVICE_NAME has been created and is ready to be started.\n" # Optionally, enable and start the service @@ -121,12 +121,12 @@ remove_service() { if [ -f "$SERVICE_FILE" ]; then printf "Stopping and disabling the service...\n" - $ESCALATION_TOOL systemctl stop "$SERVICE_NAME" - $ESCALATION_TOOL systemctl disable "$SERVICE_NAME" + "$ESCALATION_TOOL" systemctl stop "$SERVICE_NAME" + "$ESCALATION_TOOL" systemctl disable "$SERVICE_NAME" printf "Removing the service file...\n" - $ESCALATION_TOOL rm -f "$SERVICE_FILE" - $ESCALATION_TOOL systemctl daemon-reload + "$ESCALATION_TOOL" rm -f "$SERVICE_FILE" + "$ESCALATION_TOOL" systemctl daemon-reload printf "Service $SERVICE_NAME has been removed.\n" else printf "Service $SERVICE_NAME does not exist.\n" @@ -139,7 +139,7 @@ start_service() { printf "Enter the name of the service to start (e.g., my_service):\n" read -r SERVICE_NAME - if $ESCALATION_TOOL systemctl start "$SERVICE_NAME"; then + if "$ESCALATION_TOOL" systemctl start "$SERVICE_NAME"; then printf "Service $SERVICE_NAME has been started.\n" else printf "Failed to start service: $SERVICE_NAME.\n" @@ -152,7 +152,7 @@ stop_service() { printf "Enter the name of the service to stop (e.g., my_service):\n" read -r SERVICE_NAME - if $ESCALATION_TOOL systemctl stop "$SERVICE_NAME"; then + if "$ESCALATION_TOOL" systemctl stop "$SERVICE_NAME"; then printf "Service $SERVICE_NAME has been stopped.\n" else printf "Failed to stop service: $SERVICE_NAME.\n" @@ -165,7 +165,7 @@ enable_service() { printf "Enter the name of the service to enable (e.g., my_service):\n" read -r SERVICE_NAME - if $ESCALATION_TOOL systemctl enable "$SERVICE_NAME"; then + if "$ESCALATION_TOOL" systemctl enable "$SERVICE_NAME"; then printf "Service $SERVICE_NAME has been enabled.\n" else printf "Failed to enable service: $SERVICE_NAME.\n" @@ -178,7 +178,7 @@ disable_service() { printf "Enter the name of the service to disable (e.g., my_service):\n" read -r SERVICE_NAME - if $ESCALATION_TOOL systemctl disable "$SERVICE_NAME"; then + if "$ESCALATION_TOOL" systemctl disable "$SERVICE_NAME"; then printf "Service $SERVICE_NAME has been enabled.\n" else printf "Failed to enable service: $SERVICE_NAME.\n" @@ -228,8 +228,8 @@ create_service_from_external() { "$ESCALATION_TOOL" cp "$SERVICE_FILE" "$SYSTEMD_SERVICE_FILE" # Set permissions and reload systemd - $ESCALATION_TOOL chmod 644 "$SYSTEMD_SERVICE_FILE" - $ESCALATION_TOOL systemctl daemon-reload + "$ESCALATION_TOOL" chmod 644 "$SYSTEMD_SERVICE_FILE" + "$ESCALATION_TOOL" systemctl daemon-reload printf "Service $SERVICE_NAME has been created and is ready to be started.\n" # Optionally, enable and start the service @@ -237,8 +237,8 @@ create_service_from_external() { read -r START_ENABLE if [ "$START_ENABLE" = "y" ]; then - $ESCALATION_TOOL systemctl start "$SERVICE_NAME" - $ESCALATION_TOOL systemctl enable "$SERVICE_NAME" + "$ESCALATION_TOOL" systemctl start "$SERVICE_NAME" + "$ESCALATION_TOOL" systemctl enable "$SERVICE_NAME" printf "Service $SERVICE_NAME has been started and enabled.\n" else printf "Service $SERVICE_NAME has been created but not started.\n" diff --git a/tabs/utils/ssh.sh b/tabs/utils/ssh.sh new file mode 100644 index 000000000..98b8715ce --- /dev/null +++ b/tabs/utils/ssh.sh @@ -0,0 +1,250 @@ +#!/bin/sh -e + +. ../common-script.sh + +# Check if ~/.ssh/config exists, if not, create it +if [ ! -f ~/.ssh/config ]; then + touch ~/.ssh/config + chmod 600 ~/.ssh/config +fi + +# Function to show available hosts from ~/.ssh/config +show_available_hosts() { + printf "%b\n" "Available Systems:" + grep -E "^Host " ~/.ssh/config | awk '{print $2}' + printf "%b\n" "-------------------" +} + +# Function to ask for host details +ask_for_host_details() { + printf "%b\n" "Enter Host Alias: " + read -r host_alias + printf "%b\n" "Enter Remote Host (hostname or IP): " + read -r host + printf "%b\n" "Enter Remote User: " + read -r user + printf "%b\n" "Host $host_alias" >> ~/.ssh/config + printf "%b\n" " HostName $host" >> ~/.ssh/config + printf "%b\n" " User $user" >> ~/.ssh/config + printf "%b\n" " IdentityFile ~/.ssh/id_rsa" >> ~/.ssh/config + printf "%b\n" " StrictHostKeyChecking no" >> ~/.ssh/config + printf "%b\n" " UserKnownHostsFile=/dev/null" >> ~/.ssh/config + printf "%b\n" "Host $host_alias added successfully." +} + +# Function to generate SSH key if not exists +generate_ssh_key() { + if [ ! -f ~/.ssh/id_rsa ]; then + printf "%b\n" "SSH key not found, generating one..." + ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" -C "$(whoami)@$(hostname)" + else + printf "%b\n" "SSH key already exists." + fi +} + +# Function to share the SSH public key with the remote host +share_ssh_key() { + printf "%b\n" "Enter the alias of the host to copy the key to: " + read -r host_alias + printf "%b\n" "Copying SSH key to $host_alias..." + ssh-copy-id "$host_alias" + printf "%b\n" "SSH key copied to $host_alias successfully." +} + +# Function to disable password authentication and allow only SSH keys +#repeated twice as changes should take place when in commented state or modified state. +disable_password_auth() { + printf "%b\n" "Disabling SSH password authentication and enabling key-only login..." + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" + ssh $host_alias " + "$ESCALATION_TOOL" -S sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^#PubkeyAuthentication no/PubkeyAuthentication yes/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^PubkeyAuthentication no/PubkeyAuthentication yes/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S systemctl restart sshd + " + printf "%b\n" "PasswordAuthentication set to no and PubkeyAuthentication set to yes." +} + +enable_password_auth() { + printf "%b\n" "Disabling SSH password authentication and enabling key-only login..." + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "\n" + ssh $host_alias " + "$ESCALATION_TOOL" -S sed -i 's/^#PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication no/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S sed -i 's/^PubkeyAuthentication yes/PubkeyAuthentication no/' /etc/ssh/sshd_config && + "$ESCALATION_TOOL" -S systemctl restart sshd + " + printf "%b\n" "PasswordAuthentication set to yes and PubkeyAuthentication set to no." +} + +# Function to check if password authentication is disabled +check_password_auth() { + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + ssh $host_alias "grep '^PasswordAuthentication' /etc/ssh/sshd_config" +} + +# Function to run a command on a remote server +run_remote_command() { + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the command to run: " + read -r remote_command + ssh $host_alias "$remote_command" +} + +# Function to copy a file to a remote server +copy_file_to_remote() { + printf "%b\n" "Enter the local file path: " + read -r local_file + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the remote destination path: " + read -r remote_path + scp $local_file $host_alias:$remote_path +} + +# Function to copy a directory to a remote server +copy_directory_to_remote() { + printf "%b\n" "Enter the local directory path: " + read -r local_dir + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the remote destination path: " + read -r remote_path + scp -r $local_dir $host_alias:$remote_path +} + + +# Function to move a file to a remote server (copy and delete local) +move_file_to_remote() { + printf "%b\n" "Enter the local file path: " + read -r local_file + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the remote destination path: " + read -r remote_path + scp $local_file $host_alias:$remote_path && rm $local_file +} + +# Function to move a directory to a remote server (copy and delete local) +move_directory_to_remote() { + printf "%b\n" "Enter the local directory path: " + read -r local_dir + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the remote destination path: " + read -r remote_path + scp -r $local_dir $host_alias:$remote_path && rm -r $local_dir +} + +# Function to remove a system from SSH configuration +remove_system() { + printf "%b\n" "Enter the alias of the host to remove: " + read -r host_alias + sed -i "/^Host $host_alias/,+3d" ~/.ssh/config + printf "%b\n" "Removed $host_alias from SSH configuration." +} + +# Function to view SSH configuration +view_ssh_config() { + printf "%b\n" "Enter the alias of the host to view (or press Enter to view all): " + read -r host_alias + if [ -z "$host_alias" ]; then + cat ~/.ssh/config + else + grep -A 3 "^Host $host_alias" ~/.ssh/config + fi +} + +# Function to backup files from remote host +backup_files() { + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the files or directories to backup on remote host: " + read -r remote_files + printf "%b\n" "Enter the local backup directory path: " + read -r local_backup_dir + scp -r $host_alias:$remote_files $local_backup_dir +} + +# Function to sync directories with remote host +sync_directories() { + printf "%b\n" "Enter the local directory path: " + read -r local_dir + printf "%b\n" "Enter the alias of the host: " + read -r host_alias + printf "%b\n" "Enter the remote directory path: " + read -r remote_dir + rsync -avz $local_dir $host_alias:$remote_dir +} + +# Function to check SSH key authentication status +check_ssh_key_authentication() { + printf "%b\n""Enter the alias of the host: " + read -r host_alias + ssh $host_alias "grep '^PubkeyAuthentication' /etc/ssh/sshd_config" +} + +# Function to show options for the user +show_menu() { + printf "%b\n" "Select an SSH operation:" + printf "%b\n" "1. Add a new system" + printf "%b\n" "2. Connect to a system" + printf "%b\n" "3. Generate SSH key" + printf "%b\n" "4. Share SSH key with remote host" + printf "%b\n" "5. Disable password authentication on remote host" + printf "%b\n" "6. Enable password authentication on remote host" + printf "%b\n" "7. Check password authentication on remote host" + printf "%b\n" "8. Check SSH key authentication status" + printf "%b\n" "9. Run a command on remote host" + printf "%b\n" "10. Copy a file to remote host" + printf "%b\n" "11. Copy a directory to remote host" + printf "%b\n" "12. Move a file to remote host (copy and delete local)" + printf "%b\n" "13. Move a directory to remote host (copy and delete local)" + printf "%b\n" "14. Remove a system from SSH configuration" + printf "%b\n" "15. View SSH configuration" + printf "%b\n" "16. Backup files from remote host" + printf "%b\n" "17. Sync directories with remote host" + printf "%b\n" "18. Exit" + printf "%b\n" "Enter your choice: " +} + +# Function to execute the selected SSH operation +main() { + while true; do + show_menu + read choice + case $choice in + 1) ask_for_host_details ;; + 2) show_available_hosts && printf "%b\n" "Enter the alias of the host to connect to: " && read -r host_alias; ssh $host_alias ;; + 3) generate_ssh_key ;; + 4) share_ssh_key ;; + 5) disable_password_auth ;; + 6) enable_password_auth ;; + 7) check_password_auth ;; + 8) check_ssh_key_authentication ;; + 9) run_remote_command ;; + 10) copy_file_to_remote ;; + 11) copy_directory_to_remote ;; + 12) move_file_to_remote ;; + 13) move_directory_to_remote ;; + 14) remove_system ;; + 15) view_ssh_config ;; + 16) backup_files ;; + 17) sync_directories ;; + 18) exit ;; + *) printf "%b\n" "Invalid choice. Please try again." ;; + esac +done +} + +checkEnv +checkEscalationTool +main \ No newline at end of file diff --git a/tabs/utils/tab_data.toml b/tabs/utils/tab_data.toml index 012ec3413..39072521a 100644 --- a/tabs/utils/tab_data.toml +++ b/tabs/utils/tab_data.toml @@ -1,9 +1,13 @@ name = "Utilities" +multi_selectable = false [[data]] -name = "WiFi Manager" -description = "This utility is designed to manage wifi in your system" -script = "wifi-control.sh" +name = "Auto Login" +script = "auto-login.sh" + +[[data]] +name = "Auto Power Profiling" +script = "power-profile.sh" [[data]] name = "Bluetooth Manager" @@ -11,14 +15,18 @@ description = "This utility is designed to manage bluetooth in your system" script = "bluetooth-control.sh" [[data]] -name = "Numlock on Startup" -description = "This utility is designed to configure auto enabling of numlock on boot" -script = "numlock.sh" +name = "Bootable USB Creator" +script = "create-bootable-usb.sh" [[data]] name = "Crypto tool" script = "encrypt_decrypt_tool.sh" +[[data]] +name = "Numlock on Startup" +description = "This utility is designed to configure auto enabling of numlock on boot" +script = "numlock.sh" + [[data]] name = "Ollama" description = "This utility is designed to manage ollama in your system" @@ -30,16 +38,13 @@ description = "This utility is designed to manage services in your system" script = "service-control.sh" [[data]] -name = "Auto Login" -script = "auto-login.sh" - -[[data]] -name = "Bootable USB Creator" -script = "create-bootable-usb.sh" +name = "WiFi Manager" +description = "This utility is designed to manage wifi in your system" +script = "wifi-control.sh" [[data]] -name = "Auto Power Profiling" -script = "power-profile.sh" +name = "SSH Commands" +script = "ssh.sh" [[data]] name = "Timeshift Backup" @@ -112,4 +117,27 @@ matches = true [[data.entries]] name = "Set Brightness" script = "monitor-control/set_brightness.sh" -matches = true \ No newline at end of file +matches = true + +[[data]] +name = "User Account Manager" + +[[data.entries]] +name = "Add User" +script = "user-account-manager/add_user.sh" + +[[data.entries]] +name = "Change Password" +script = "user-account-manager/change_password.sh" + +[[data.entries]] +name = "Delete User" +script = "user-account-manager/delete_user.sh" + +[[data.entries]] +name = "Add User To Groups" +script = "user-account-manager/add_to_group.sh" + +[[data.entries]] +name = "Remove User From Groups" +script = "user-account-manager/remove_from_group.sh" \ No newline at end of file diff --git a/tabs/utils/timeshift.sh b/tabs/utils/timeshift.sh index ec69081f2..4b47debfb 100644 --- a/tabs/utils/timeshift.sh +++ b/tabs/utils/timeshift.sh @@ -8,12 +8,12 @@ install_timeshift() { printf "%b\n" "${YELLOW}Checking if Timeshift is installed...${RC}" if ! command_exists timeshift; then - case ${PACKAGER} in + case "$PACKAGER" in pacman) - $ESCALATION_TOOL "${PACKAGER}" -S --noconfirm timeshift + "$ESCALATION_TOOL" "${PACKAGER}" -S --noconfirm timeshift ;; *) - $ESCALATION_TOOL "${PACKAGER}" install -y timeshift + "$ESCALATION_TOOL" "${PACKAGER}" install -y timeshift ;; esac else @@ -37,13 +37,13 @@ display_menu() { # Function to list snapshots list_snapshots() { printf "%b\n" "${CYAN}Listing snapshots...${RC}" - $ESCALATION_TOOL timeshift --list-snapshots + "$ESCALATION_TOOL" timeshift --list-snapshots } # Function to list devices list_devices() { printf "%b\n" "${CYAN}Listing available devices...${RC}" - $ESCALATION_TOOL timeshift --list-devices + "$ESCALATION_TOOL" timeshift --list-devices } # Function to create a new snapshot @@ -55,13 +55,13 @@ create_snapshot() { if [ -z "$COMMENT" ] && [ -z "$TAG" ]; then printf "%b\n" "${CYAN}Creating snapshot with no comment or tag...${RC}" - $ESCALATION_TOOL timeshift --create + "$ESCALATION_TOOL" timeshift --create elif [ -z "$TAG" ]; then printf "%b\n" "${CYAN}Creating snapshot with no tag...${RC}" - $ESCALATION_TOOL timeshift --create --comments "$COMMENT" + "$ESCALATION_TOOL" timeshift --create --comments "$COMMENT" else printf "%b\n" "${CYAN}Creating snapshot with tag: $TAG...${RC}" - $ESCALATION_TOOL timeshift --create --comments "$COMMENT" --tags "$TAG" + "$ESCALATION_TOOL" timeshift --create --comments "$COMMENT" --tags "$TAG" fi if [ $? -eq 0 ]; then @@ -83,11 +83,11 @@ restore_snapshot() { read -r SKIP_GRUB if [ "$SKIP_GRUB" = "yes" ]; then - $ESCALATION_TOOL timeshift --restore --snapshot "$SNAPSHOT" --target-device "$TARGET_DEVICE" --skip-grub --yes + "$ESCALATION_TOOL" timeshift --restore --snapshot "$SNAPSHOT" --target-device "$TARGET_DEVICE" --skip-grub --yes else printf "%b\n" "${CYAN}Enter GRUB device (e.g., /dev/sda): ${RC}" read -r GRUB_DEVICE - $ESCALATION_TOOL timeshift --restore --snapshot "$SNAPSHOT" --target-device "$TARGET_DEVICE" --grub-device "$GRUB_DEVICE" --yes + "$ESCALATION_TOOL" timeshift --restore --snapshot "$SNAPSHOT" --target-device "$TARGET_DEVICE" --grub-device "$GRUB_DEVICE" --yes fi if [ $? -eq 0 ]; then @@ -105,7 +105,7 @@ delete_snapshot() { read -r SNAPSHOT printf "%b\n" "${YELLOW}Deleting snapshot $SNAPSHOT...${RC}" - $ESCALATION_TOOL timeshift --delete --snapshot "$SNAPSHOT" --yes + "$ESCALATION_TOOL" timeshift --delete --snapshot "$SNAPSHOT" --yes if [ $? -eq 0 ]; then printf "%b\n" "${GREEN}Snapshot deleted successfully.${RC}" @@ -122,7 +122,7 @@ delete_all_snapshots() { if [ "$CONFIRMATION" = "yes" ]; then printf "%b\n" "${CYAN}Deleting all snapshots...${RC}" - $ESCALATION_TOOL timeshift --delete-all --yes + "$ESCALATION_TOOL" timeshift --delete-all --yes if [ $? -eq 0 ]; then printf "%b\n" "${GREEN}All snapshots deleted successfully.${RC}" else diff --git a/tabs/utils/user-account-manager/add_to_group.sh b/tabs/utils/user-account-manager/add_to_group.sh new file mode 100755 index 000000000..bc66da1b4 --- /dev/null +++ b/tabs/utils/user-account-manager/add_to_group.sh @@ -0,0 +1,37 @@ +#!/bin/sh -e + +. ../../common-script.sh +. ./utility_functions.sh + +clear +printf "%b\n" "${YELLOW}Add to group${RC}" +printf "%b\n" "${YELLOW}=================${RC}" + +username=$(promptUsername "" "non-root") || exit 1 +user_groups=$(groups "$username" | cut -d: -f2 | sort | tr '\n' ' ') + +printf "%b\n" "${YELLOW}Groups user $username is in:${RC} $user_groups" +printf "%b\n" "${YELLOW}=================${RC}" + +available_groups=$(cut -d: -f1 /etc/group | sort | tr '\n' ' ') + +printf "%b\n" "${YELLOW}Available groups:${RC} $available_groups" +printf "%b\n" "${YELLOW}=================${RC}" + +printf "%b\n" "${YELLOW}Enter the groups you want to add user $username to (space-separated):${RC} " +read -r groups + +checkEmpty "$groups" || exit 1 +checkGroupAvailabe "$groups" "$available_groups" || exit 1 + +groups_to_add=$(echo "$groups" | tr ' ' ',') + +printf "Are you sure you want to add user $username to $groups_to_add? [Y/N]: " +read -r confirm +confirmAction || exit 1 + +$ESCALATION_TOOL usermod -aG $groups_to_add "$username" + +printf "%b\n" "${GREEN}User successfully added to the $groups_to_add${RC}" + +checkEnv \ No newline at end of file diff --git a/tabs/utils/user-account-manager/add_user.sh b/tabs/utils/user-account-manager/add_user.sh new file mode 100755 index 000000000..d26dbfc65 --- /dev/null +++ b/tabs/utils/user-account-manager/add_user.sh @@ -0,0 +1,26 @@ +#!/bin/sh -e + +. ../../common-script.sh +. ./utility_functions.sh + +clear +printf "%b\n" "${YELLOW}Create a new user${RC}" +printf "%b\n" "${YELLOW}=================${RC}" + +username=$(promptUsername "add" "non-root") || exit 1 + +# Check if username is valid +if ! echo "$username" | grep '^[a-z][-a-z0-9_]*$' > /dev/null; then + printf "%b\n" "${RED}Username must only contain letters, numbers, hyphens, and underscores. It cannot start with a number or contain spaces.${RC}" + exit 1 +fi + +password=$(promptPassword) || exit 1 + +$ESCALATION_TOOL useradd -m "$username" -g users -s /bin/bash +echo "$username:$password" | "$ESCALATION_TOOL" chpasswd + +printf "%b\n" "${GREEN}User $username created successfully${RC}" +printf "%b\n" "${GREEN}To add additional groups use Add User To Groups${RC}" + +checkEnv diff --git a/tabs/utils/user-account-manager/change_password.sh b/tabs/utils/user-account-manager/change_password.sh new file mode 100755 index 000000000..677dcd759 --- /dev/null +++ b/tabs/utils/user-account-manager/change_password.sh @@ -0,0 +1,20 @@ +#!/bin/sh -e + +. ../../common-script.sh +. ./utility_functions.sh + +clear +printf "%b\n" "${YELLOW}Change password${RC}" +printf "%b\n" "${YELLOW}=================${RC}" + +username=$(promptUsername "" "root") || exit 1 +password=$(promptPassword) || exit 1 + +printf "Are you sure you want to change password for $username? [Y/N]: " +read -r confirm +confirmAction || exit 1 + +echo "$username:$password" | "$ESCALATION_TOOL" chpasswd +printf "%b\n" "${GREEN}Password changed successfully${RC}" + +checkEnv \ No newline at end of file diff --git a/tabs/utils/user-account-manager/delete_user.sh b/tabs/utils/user-account-manager/delete_user.sh new file mode 100755 index 000000000..ef1a59c49 --- /dev/null +++ b/tabs/utils/user-account-manager/delete_user.sh @@ -0,0 +1,27 @@ +#!/bin/sh -e + +. ../../common-script.sh +. ./utility_functions.sh + +clear +printf "%b\n" "${YELLOW}Delete a user${RC}" +printf "%b\n" "${YELLOW}=================${RC}" + +username=$(promptUsername "" "non-root") || exit 1 + +# Check if current user +if [ "$username" = "$USER" ]; then + printf "%b\n" "${RED}Cannot delete the current user${RC}" + printf "%b\n" "${RED}Press [Enter] to continue...${RC}" + read dummy + return +fi + +printf "Are you sure you want to delete user $username? [Y/N]: " +read -r confirm +confirmAction || exit 1 + +$ESCALATION_TOOL userdel --remove "$username" 2>/dev/null +printf "%b\n" "${GREEN}User $username deleted successfully${RC}" + +checkEnv \ No newline at end of file diff --git a/tabs/utils/user-account-manager/remove_from_group.sh b/tabs/utils/user-account-manager/remove_from_group.sh new file mode 100755 index 000000000..f56a5d9e3 --- /dev/null +++ b/tabs/utils/user-account-manager/remove_from_group.sh @@ -0,0 +1,32 @@ +#!/bin/sh -e + +. ../../common-script.sh +. ./utility_functions.sh + +clear +printf "%b\n" "${YELLOW}Remove from group${RC}" +printf "%b\n" "${YELLOW}=================${RC}" + +username=$(promptUsername "" "non-root") || exit 1 +user_groups=$(groups "$username" | cut -d: -f2 | sort | tr '\n' ' ') + +printf "%b\n" "${YELLOW}Groups user $username is in:${RC} $user_groups" +printf "%b\n" "${YELLOW}=================${RC}" + +printf "%b\n" "${YELLOW}Enter the groups you want to remove user $username from (space-separated):${RC} " +read -r groups + +checkEmpty "$groups" || exit 1 +checkGroupAvailabe "$groups" "$user_groups" || exit 1 + +groups_to_remove=$(echo "$groups" | tr ' ' ',') + +printf "Are you sure you want to remove user $username from $groups_to_remove? [Y/N]: " +read -r confirm +confirmAction || exit 1 + +$ESCALATION_TOOL usermod -rG $groups_to_remove "$username" + +printf "%b\n" "${GREEN}User successfully removed from $groups_to_remove${RC}" + +checkEnv \ No newline at end of file diff --git a/tabs/utils/user-account-manager/utility_functions.sh b/tabs/utils/user-account-manager/utility_functions.sh new file mode 100755 index 000000000..b6af5f538 --- /dev/null +++ b/tabs/utils/user-account-manager/utility_functions.sh @@ -0,0 +1,103 @@ +#!/bin/sh -e + +. ../../common-script.sh + +# Prompt for username +promptUsername() { + printf "Enter the username: " + read -r username + + checkEmpty "$username"; + + if [ "$1" = "add" ]; then + checkUserExistence "$username" "$1" + else + checkUserExistence "$username" "$1" + checkReservedUsername "$username" "$2" + fi + echo "$username" +} + + +# Prompt for password +promptPassword() { + stty -echo + printf "Enter the password (PASSWORD IS HIDDEN): " + read -r password1 + echo >&2 + printf "Re-enter the password (PASSWORD IS HIDDEN): " + read -r password2 + echo >&2 + stty echo + + if ! checkEmpty "$password1"; then + promptPassword + fi + + if [ "$password1" != "$password2" ]; then + printf "%b\n" "${RED}Passwords do not match${RC}" >&2 + promptPassword + else + echo $password1 + fi +} + +# Check if input is empty +checkEmpty() { + if [ -z "$1" ]; then + printf "%b\n" "${RED}Empty value is not allowed${RC}" >&2 + exit 1 + fi +} + +# Check if user exists +checkUserExistence() { + if [ "$2" = "add" ]; then + if id "$1" > /dev/null 2>&1; then + printf "%b\n" "${RED}User already exists${RC}" >&2 + exit 1 + fi + else + if ! id "$1" > /dev/null 2>&1; then + printf "%b\n" "${RED}User does not exist${RC}" >&2 + exit 1 + fi + fi +} + +# Check if user is reserved +checkReservedUsername() { + uid=$(id -u "$1") + if [ "$2" = "root" ]; then + if [ "$uid" -le 999 ] && [ "$uid" -ne 0 ]; then + printf "%b\n" "${RED}Cannot modify system users${RC}" >&2 + exit 1 + fi + else + if [ "$(id -u "$1")" -le 999 ]; then + printf "%b\n" "${RED}Cannot modify system users${RC}" >&2 + exit 1 + fi + fi +} + +# Check if user is reserved +confirmAction() { + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + printf "%b\n" "${RED}Cancelled operation...${RC}" >&2 + exit 1 + fi +} + +# Check if group is available +checkGroupAvailabe() { + for group in $1; do + if ! echo "$2" | grep -wq "$group"; then + printf "%b\n" "${RED}Group $group not avaiable${RC}" >&2 + exit 1 + fi + done +} + +checkEnv +checkEscalationTool \ No newline at end of file diff --git a/tui/src/floating_text.rs b/tui/src/floating_text.rs index fd53ed074..878066f79 100644 --- a/tui/src/floating_text.rs +++ b/tui/src/floating_text.rs @@ -8,7 +8,7 @@ use ratatui::{ layout::Rect, style::{Style, Stylize}, text::Line, - widgets::{Block, Borders, List}, + widgets::{Block, Borders, Clear, List}, Frame, }; pub enum FloatingTextMode { @@ -84,7 +84,7 @@ impl FloatContent for FloatingText { let inner_area = block.inner(area); // Create the list of lines to be displayed - let mut lines: Vec = self + let lines: Vec = self .text .iter() .skip(self.scroll) @@ -102,15 +102,14 @@ impl FloatContent for FloatingText { .map(Line::from) .collect(); - // Prevents background text from appearing after the floating content - while lines.len() < inner_area.height as usize { - lines.push(Line::from(" ".repeat(inner_area.width as usize))); - } // Create list widget let list = List::new(lines) .block(Block::default()) .highlight_style(Style::default().reversed()); + // Clear the text underneath the floats rendered area + frame.render_widget(Clear, inner_area); + // Render the list inside the bordered area frame.render_widget(list, inner_area); } diff --git a/tui/src/hint.rs b/tui/src/hint.rs index ce6500cb1..034406dbc 100644 --- a/tui/src/hint.rs +++ b/tui/src/hint.rs @@ -96,11 +96,18 @@ impl Shortcut { } } -fn get_list_item_shortcut(state: &AppState) -> Shortcut { +fn get_list_item_shortcut(state: &AppState) -> Vec { if state.selected_item_is_dir() { - Shortcut::new(vec!["l", "Right", "Enter"], "Go to selected dir") + vec![Shortcut::new( + vec!["l", "Right", "Enter"], + "Go to selected dir", + )] } else { - Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command") + vec![ + Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command"), + Shortcut::new(vec!["p"], "Enable preview"), + Shortcut::new(vec!["d"], "Command Description"), + ] } } @@ -110,12 +117,14 @@ pub fn draw_shortcuts(state: &AppState, frame: &mut Frame, area: Rect) { scope_name: "Search bar", hints: vec![Shortcut::new(vec!["Enter"], "Finish search")], }, + Focus::List => { let mut hints = Vec::new(); hints.push(Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil")); + if state.at_root() { - hints.push(Shortcut::new(vec!["h", "Left", "Tab"], "Focus tab list")); - hints.push(get_list_item_shortcut(state)); + hints.push(Shortcut::new(vec!["h", "Left"], "Focus tab list")); + hints.extend(get_list_item_shortcut(state)); } else { if state.selected_item_is_up_dir() { hints.push(Shortcut::new( @@ -124,34 +133,39 @@ pub fn draw_shortcuts(state: &AppState, frame: &mut Frame, area: Rect) { )); } else { hints.push(Shortcut::new(vec!["h", "Left"], "Go to parent directory")); - hints.push(get_list_item_shortcut(state)); - if state.selected_item_is_cmd() { - hints.push(Shortcut::new(vec!["p"], "Enable preview")); - hints.push(Shortcut::new(vec!["d"], "Command Description")); - } + hints.extend(get_list_item_shortcut(state)); } - hints.push(Shortcut::new(vec!["Tab"], "Focus tab list")); }; hints.push(Shortcut::new(vec!["k", "Up"], "Select item above")); hints.push(Shortcut::new(vec!["j", "Down"], "Select item below")); hints.push(Shortcut::new(vec!["t"], "Next theme")); hints.push(Shortcut::new(vec!["T"], "Previous theme")); + if state.is_current_tab_multi_selectable() { + hints.push(Shortcut::new(vec!["v"], "Toggle multi-selection mode")); + hints.push(Shortcut::new(vec!["Space"], "Select multiple commands")); + } + hints.push(Shortcut::new(vec!["Tab"], "Next tab")); + hints.push(Shortcut::new(vec!["Shift-Tab"], "Previous tab")); ShortcutList { scope_name: "Item list", hints, } } + Focus::TabList => ShortcutList { scope_name: "Tab list", hints: vec![ Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"), - Shortcut::new(vec!["l", "Right", "Tab", "Enter"], "Focus action list"), + Shortcut::new(vec!["l", "Right", "Enter"], "Focus action list"), Shortcut::new(vec!["k", "Up"], "Select item above"), Shortcut::new(vec!["j", "Down"], "Select item below"), Shortcut::new(vec!["t"], "Next theme"), Shortcut::new(vec!["T"], "Previous theme"), + Shortcut::new(vec!["Tab"], "Next tab"), + Shortcut::new(vec!["Shift-Tab"], "Previous tab"), ], }, + Focus::FloatingWindow(ref float) => float.get_shortcut_list(), } .draw(frame, area); diff --git a/tui/src/running_command.rs b/tui/src/running_command.rs index d8df34028..c605b204a 100644 --- a/tui/src/running_command.rs +++ b/tui/src/running_command.rs @@ -136,25 +136,30 @@ impl FloatContent for RunningCommand { } impl RunningCommand { - pub fn new(command: Command) -> Self { + pub fn new(commands: Vec) -> Self { let pty_system = NativePtySystem::default(); // Build the command based on the provided Command enum variant - let mut cmd = CommandBuilder::new("sh"); - match command { - Command::Raw(prompt) => { - cmd.arg("-c"); - cmd.arg(prompt); - } - Command::LocalFile(file) => { - cmd.arg(&file); - if let Some(parent) = file.parent() { - cmd.cwd(parent); + let mut cmd: CommandBuilder = CommandBuilder::new("sh"); + cmd.arg("-c"); + + // All the merged commands are passed as a single argument to reduce the overhead of rebuilding the command arguments for each and every command + let mut script = String::new(); + for command in commands { + match command { + Command::Raw(prompt) => script.push_str(&format!("{}\n", prompt)), + Command::LocalFile(file) => { + if let Some(parent) = file.parent() { + script.push_str(&format!("cd {}\n", parent.display())); + } + script.push_str(&format!("sh {}\n", file.display())); } + Command::None => panic!("Command::None was treated as a command"), } - Command::None => panic!("Command::None was treated as a command"), } + cmd.arg(script); + // Open a pseudo-terminal with initial size let pair = pty_system .openpty(PtySize { diff --git a/tui/src/state.rs b/tui/src/state.rs index 2d13e399d..f3e1c5741 100644 --- a/tui/src/state.rs +++ b/tui/src/state.rs @@ -36,6 +36,8 @@ pub struct AppState { /// widget selection: ListState, filter: Filter, + multi_select: bool, + selected_commands: Vec, drawable: bool, } @@ -64,6 +66,8 @@ impl AppState { visit_stack: vec![root_id], selection: ListState::default().with_selected(Some(0)), filter: Filter::new(), + multi_select: false, + selected_commands: Vec::new(), drawable: false, }; state.update_items(); @@ -200,12 +204,29 @@ impl AppState { |ListEntry { node, has_children, .. }| { + let is_selected = self.selected_commands.contains(&node.command); + let (indicator, style) = if is_selected { + (self.theme.multi_select_icon(), Style::default().bold()) + } else { + ("", Style::new()) + }; if *has_children { - Line::from(format!("{} {}", self.theme.dir_icon(), node.name)) - .style(self.theme.dir_color()) + Line::from(format!( + "{} {} {}", + self.theme.dir_icon(), + node.name, + indicator + )) + .style(self.theme.dir_color()) } else { - Line::from(format!("{} {}", self.theme.cmd_icon(), node.name)) - .style(self.theme.cmd_color()) + Line::from(format!( + "{} {} {}", + self.theme.cmd_icon(), + node.name, + indicator + )) + .style(self.theme.cmd_color()) + .patch_style(style) } }, )); @@ -218,12 +239,20 @@ impl AppState { // Create the list widget with items let list = List::new(items) - .highlight_style(style) - .block( - Block::default() - .borders(Borders::ALL) - .title(format!("Linux Toolbox - {}", env!("BUILD_DATE"))), - ) + .highlight_style(if let Focus::List = self.focus { + Style::default().reversed() + } else { + Style::new() + }) + .block(Block::default().borders(Borders::ALL).title(format!( + "Linux Toolbox - {} {}", + env!("BUILD_DATE"), + if self.multi_select { + "[Multi-Select]" + } else { + "" + } + ))) .scroll_padding(1); frame.render_stateful_widget(list, chunks[1], &mut self.selection); @@ -250,6 +279,31 @@ impl AppState { return true; } + // Handle key only when Tablist or List is focused + // Prevents exiting the application even when a command is running + // Add keys here which should work on both TabList and List + if matches!(self.focus, Focus::TabList | Focus::List) { + match key.code { + KeyCode::Tab => { + if self.current_tab.selected().unwrap() == self.tabs.len() - 1 { + self.current_tab.select_first(); + } else { + self.current_tab.select_next(); + } + self.refresh_tab(); + } + KeyCode::BackTab => { + if self.current_tab.selected().unwrap() == 0 { + self.current_tab.select(Some(self.tabs.len() - 1)); + } else { + self.current_tab.select_previous(); + } + self.refresh_tab(); + } + _ => {} + } + } + match &mut self.focus { Focus::FloatingWindow(command) => { if command.handle_key_event(key) { @@ -262,9 +316,7 @@ impl AppState { _ => {} }, Focus::TabList => match key.code { - KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => { - self.focus = Focus::List - } + KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List, KeyCode::Char('j') | KeyCode::Down if self.current_tab.selected().unwrap() + 1 < self.tabs.len() => @@ -286,8 +338,8 @@ impl AppState { Focus::List if key.kind != KeyEventKind::Release => match key.code { KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(), KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(), - KeyCode::Char('p') => self.enable_preview(), - KeyCode::Char('d') => self.enable_description(), + KeyCode::Char('p') | KeyCode::Char('P') => self.enable_preview(), + KeyCode::Char('d') | KeyCode::Char('D') => self.enable_description(), KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.handle_enter(), KeyCode::Char('h') | KeyCode::Left => { if self.at_root() { @@ -297,22 +349,49 @@ impl AppState { } } KeyCode::Char('/') => self.enter_search(), - KeyCode::Tab => self.focus = Focus::TabList, KeyCode::Char('t') => self.theme.next(), KeyCode::Char('T') => self.theme.prev(), + KeyCode::Char('v') | KeyCode::Char('V') => self.toggle_multi_select(), + KeyCode::Char(' ') if self.multi_select => self.toggle_selection(), _ => {} }, _ => (), }; true } - + fn toggle_multi_select(&mut self) { + if self.is_current_tab_multi_selectable() { + self.multi_select = !self.multi_select; + if !self.multi_select { + self.selected_commands.clear(); + } + } + } + fn toggle_selection(&mut self) { + if let Some(command) = self.get_selected_command() { + if self.selected_commands.contains(&command) { + self.selected_commands.retain(|c| c != &command); + } else { + self.selected_commands.push(command); + } + } + } + pub fn is_current_tab_multi_selectable(&self) -> bool { + let index = self.current_tab.selected().unwrap_or(0); + self.tabs + .get(index) + .map_or(false, |tab| tab.multi_selectable) + } fn update_items(&mut self) { self.filter.update_items( &self.tabs, self.current_tab.selected().unwrap(), *self.visit_stack.last().unwrap(), ); + if !self.is_current_tab_multi_selectable() { + self.multi_select = false; + self.selected_commands.clear(); + } } /// Checks either the current tree node is the root node (can we go up the tree or no) @@ -416,9 +495,15 @@ impl AppState { } fn handle_enter(&mut self) { - if let Some(cmd) = self.get_selected_command() { - let command = RunningCommand::new(cmd); + if self.selected_item_is_cmd() { + if self.selected_commands.is_empty() { + if let Some(cmd) = self.get_selected_command() { + self.selected_commands.push(cmd); + } + } + let command = RunningCommand::new(self.selected_commands.clone()); self.spawn_float(command, 80, 80); + self.selected_commands.clear(); } else { self.go_to_selected_dir(); } diff --git a/tui/src/theme.rs b/tui/src/theme.rs index 84fa15b4d..8337645a2 100644 --- a/tui/src/theme.rs +++ b/tui/src/theme.rs @@ -56,6 +56,13 @@ impl Theme { } } + pub fn multi_select_icon(&self) -> &'static str { + match self { + Theme::Default => "", + Theme::Compatible => "*", + } + } + pub fn success_color(&self) -> Color { match self { Theme::Default => Color::Rgb(199, 55, 44),