diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2d563e0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,65 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +--- + +## [0.4.0] - 2026-03-18 + +### Added + +* Structured logging system with timestamps +* Log levels: INFO, OK, ERR +* Central log file: `/opt/update-manager/log/update-manager.log` +* Log submenu in UI +* Log viewing options: + + * Full log + * Live log (tail -f) + * Last 20 lines + * Log file location + +### Changed + +* Refactored UI to group log features into submenu +* Improved menu structure and usability + +### Improved + +* Better error visibility for SSH failures +* Consistent output across hosts +* README updated with UI screenshots and logging documentation + +--- + +## [0.3.0] - 2026-03-18 + +### Added + +* CLI menu (`update-manager-ui.sh`) +* Interactive host management +* Dialog-based UI navigation + +### Changed + +* Project renamed from `lanx-update` to `update-manager` +* Moved runtime files to `/opt/update-manager` + +--- + +## [0.2.0] - 2026-03-18 + +### Added + +* SSH-based update checking across multiple hosts +* Support for `hosts.conf` +* Config file support (`/etc/update-manager.conf`) + +--- + +## [0.1.0] - 2026-03-17 + +### Added + +* Initial version +* Basic apt update check functionality diff --git a/README.md b/README.md index 36fcbfb..dc3900b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# 🖥️ Update Manager +

+ +

+ Simple CLI tool to check and manage updates across multiple Ubuntu systems over SSH. @@ -26,10 +29,10 @@ Built for Lanx environments – lightweight, fast and no unnecessary dependencie ## Features * Check updates on multiple hosts -* Run checks remotely over SSH +* Run updates remotely over SSH +* Interactive CLI menu (dialog-based UI) +* Centralized logging * Simple config files -* Built-in logging (file + terminal) -* Dialog-based UI menu * No agents required * Works with existing SSH setup @@ -37,12 +40,20 @@ Built for Lanx environments – lightweight, fast and no unnecessary dependencie ## Update Manager UI +### Main menu +

- Update Manager CLI UI + Main menu +

+ +### Log menu + +

+ Log menu

- Lightweight • Terminal-based • Works over SSH + Lightweight • No dependencies • Works over SSH

--- @@ -57,7 +68,7 @@ sudo apt update sudo apt install dialog openssh-client sudo mkdir -p /opt/update-manager -sudo cp update-manager.sh update-manager-ui.sh /opt/update-manager/ +sudo cp update-manager.sh update-manager-ui.sh dialogrc /opt/update-manager/ sudo chmod +x /opt/update-manager/update-manager.sh sudo chmod +x /opt/update-manager/update-manager-ui.sh @@ -134,6 +145,12 @@ server3 192.168.1.30 user ## Usage +### Check updates + +```bash +update-manager check +``` + ### Start UI ```bash @@ -142,42 +159,25 @@ update-manager-ui --- -### Check updates (CLI) - -```bash -update-manager check -``` - ---- - ## Logging -The tool logs both to terminal and file. - -### Primary location +Log file location: ```bash /opt/update-manager/log/update-manager.log ``` -### Fallback location +View log: ```bash -~/update-manager/log/update-manager.log +less /opt/update-manager/log/update-manager.log ``` -### Notes +Follow log: -* Log directory is created automatically -* Log file is created automatically -* Output is written to both terminal and file -* Log levels: - - * INFO - * WARN - * ERROR - -Logs can be viewed directly from the UI. +```bash +tail -f /opt/update-manager/log/update-manager.log +``` --- @@ -189,8 +189,8 @@ Logs can be viewed directly from the UI. ├── update-manager-ui.sh ├── update-manager.conf ├── hosts.conf -└── log/ - └── update-manager.log +├── log/ +│ └── update-manager.log ``` --- @@ -199,6 +199,7 @@ Logs can be viewed directly from the UI. * Uses SSH to connect to each host * Runs `apt` commands remotely +* Logs results locally * No agents or services needed * Designed for simple and efficient operations @@ -209,7 +210,6 @@ Logs can be viewed directly from the UI. * SSH access to all hosts * SSH keys recommended (no password prompts) * Ubuntu/Debian-based systems -* `dialog` (for UI) --- @@ -218,7 +218,6 @@ Logs can be viewed directly from the UI. * 🔔 Notifications (ntfy / push alerts) * 🌐 Web interface * 📧 Email reporting -* 📜 Advanced logging / audit trail * 📊 Basic monitoring (status, last check, pending updates) * 🧩 Plugin system (extensible modules) * 🔐 Security & compliance checks @@ -226,6 +225,15 @@ Logs can be viewed directly from the UI. --- +## ❤️ Credits + +Built with ❤️ for Lanx by [NodeFox 🦊](https://nodefox.lanx.dk) +Maintained by [Eddie Nielsen](https://lanx.dk) + +> Learn. Adopt. Survive. Share. + +--- + ## License This project is licensed under the GNU GPL v3 License. @@ -233,10 +241,3 @@ This project is licensed under the GNU GPL v3 License. See the LICENSE file for full details. --- - -## Author - -Built with ❤️ for [Lanx](https://lanx.dk) by **NodeFox** 🦊 - -Maintained by Eddie Nielsen -Feel free to contribute, suggest improvements or fork the project. diff --git a/dialogrc b/dialogrc new file mode 100644 index 0000000..250e64a --- /dev/null +++ b/dialogrc @@ -0,0 +1,32 @@ +use_shadow = OFF +use_colors = ON + +screen_color = (WHITE,BLUE,OFF) +dialog_color = (WHITE,BLUE,OFF) +title_color = (YELLOW,BLUE,ON) +border_color = (WHITE,BLUE,OFF) + +button_active_color = (BLACK,WHITE,ON) +button_inactive_color = (WHITE,BLUE,OFF) + +button_key_active_color = (BLACK,WHITE,ON) +button_key_inactive_color = (WHITE,BLUE,OFF) + +button_label_active_color = (BLACK,WHITE,ON) +button_label_inactive_color = (WHITE,BLUE,OFF) + +menubox_color = (WHITE,BLUE,OFF) +menubox_border_color = (WHITE,BLUE,OFF) + +item_color = (WHITE,BLUE,OFF) +item_selected_color = (BLACK,WHITE,ON) + +tag_color = (WHITE,BLUE,OFF) +tag_selected_color = (BLACK,WHITE,ON) + +tag_key_color = (YELLOW,BLUE,OFF) +tag_key_selected_color = (BLACK,WHITE,ON) + +position_indicator_color = (WHITE,BLUE,OFF) +uarrow_color = (WHITE,BLUE,OFF) +darrow_color = (WHITE,BLUE,OFF) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..3c9771c --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,211 @@ +

+ Architecture Diagram +

+ +# 🧠 Update Manager Architecture + +This document describes how Update Manager is structured internally and how data flows through the system. + +--- + +## Overview + +Update Manager is a lightweight, SSH-based system for managing updates across multiple Linux hosts. + +It consists of two main layers: + +* **UI layer** – interactive menu (`update-manager-ui.sh`) +* **Core engine** – update logic (`update-manager.sh`) + +The system is designed to be simple, transparent, and dependency-light. + +--- + +## High-Level Flow + +```text +User + ↓ +UI (update-manager-ui.sh) + ↓ +Core (update-manager.sh) + ↓ +SSH → Remote Hosts + ↓ +Results + ↓ +Logging (/opt/update-manager/log) + ↓ +UI / User +``` + +--- + +## Components + +### UI Layer (`update-manager-ui.sh`) + +Responsible for: + +* Displaying menu (via `dialog`) +* Handling user input +* Managing hosts (add/remove/edit) +* Providing access to logs +* Delegating actions to core engine + +The UI does **not perform updates directly**. + +--- + +### Core Engine (`update-manager.sh`) + +Responsible for: + +* Reading configuration +* Parsing hosts file +* Executing SSH commands +* Checking for updates (`apt list --upgradable`) +* Handling connection errors +* Writing structured logs + +This is the **execution layer** of the system. + +--- + +### Hosts Configuration + +File: + +```bash +/opt/update-manager/hosts.conf +``` + +Format: + +```text +name ip user +``` + +Example: + +```text +server1 192.168.1.10 user +server2 192.168.1.20 user +server3 192.168.1.30 user +``` + +Each line represents a target system accessed via SSH. + +--- + +### Logging System + +Log file: + +```bash +/opt/update-manager/log/update-manager.log +``` + +Log format: + +```text +YYYY-MM-DD HH:MM:SS [LEVEL] host - message +``` + +Example: + +```text +2026-03-18 20:45:01 [INFO] lanx-www - Checking updates +2026-03-18 20:45:03 [OK] lanx-www - 3 updates available +2026-03-18 20:45:05 [ERR] lanx-db - Connection failed +``` + +#### Log Levels + +* `INFO` – informational messages +* `OK` – successful operations +* `ERR` – errors or failures + +Logging is centralized and written locally. + +--- + +## Execution Flow + +For each host: + +1. Read host entry from `hosts.conf` +2. Establish SSH connection +3. Execute: + +```bash +apt list --upgradable +``` + +4. Parse output +5. Determine: + + * Up-to-date + * Updates available + * Connection failure +6. Write result to: + + * terminal output + * log file + +--- + +## Configuration + +Primary config: + +```bash +/etc/update-manager.conf +``` + +Fallback: + +```bash +./update-manager.conf +``` + +Supported options: + +* `HOSTS_FILE` +* `SSH_OPTIONS` + +--- + +## Design Principles + +* **No agents** – uses standard SSH only +* **Simple over complex** – minimal dependencies +* **Transparent behavior** – everything is visible/logged +* **CLI-first** – designed for terminal environments +* **Modular growth** – prepared for future extensions + +--- + +## Future Architecture Direction + +Planned extensions: + +* Plugin system (modular features) +* Web interface (optional UI layer) +* Notification system (alerts) +* Metrics / monitoring +* Integration with Lanx AI + +--- + +## Summary + +Update Manager follows a clean separation: + +* UI = interaction +* Core = execution +* Config = data +* Log = output + +This keeps the system predictable, debuggable, and easy to extend. + diff --git a/docs/images/architecture-diagram.png b/docs/images/architecture-diagram.png new file mode 100644 index 0000000..d2b79f2 Binary files /dev/null and b/docs/images/architecture-diagram.png differ diff --git a/docs/images/menu-logs.png b/docs/images/menu-logs.png new file mode 100644 index 0000000..1bffefe Binary files /dev/null and b/docs/images/menu-logs.png differ diff --git a/docs/images/menu-main.png b/docs/images/menu-main.png new file mode 100644 index 0000000..80e1e80 Binary files /dev/null and b/docs/images/menu-main.png differ diff --git a/docs/images/update-manager-ui.png b/docs/images/update-manager-ui.png new file mode 100644 index 0000000..a9dd924 Binary files /dev/null and b/docs/images/update-manager-ui.png differ diff --git a/images/updatemanager-logo.png b/images/updatemanager-logo.png new file mode 100644 index 0000000..bd5a27a Binary files /dev/null and b/images/updatemanager-logo.png differ diff --git a/log/update-manager.sh b/log/update-manager.sh new file mode 100644 index 0000000..d3e9635 --- /dev/null +++ b/log/update-manager.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +UPDATE_MANAGER_SCRIPT="/opt/update-manager/update-manager.sh" + +get_log_file() { + local primary="/opt/update-manager/log/update-manager.log" + local fallback="$HOME/update-manager/log/update-manager.log" + + if [[ -f "$primary" ]]; then + echo "$primary" + else + echo "$fallback" + fi +} + +run_check() { + echo + bash "$UPDATE_MANAGER_SCRIPT" check + echo + read -rp "Press Enter to continue..." +} + +view_log() { + local log_file + log_file="$(get_log_file)" + + echo + if [[ -f "$log_file" ]]; then + less "$log_file" + else + echo "Log file not found: $log_file" + echo + read -rp "Press Enter to continue..." + fi +} + +follow_log() { + local log_file + log_file="$(get_log_file)" + + echo + if [[ -f "$log_file" ]]; then + echo "Following log: $log_file" + echo "Press Ctrl+C to stop." + echo + tail -f "$log_file" + else + echo "Log file not found: $log_file" + fi + + echo + read -rp "Press Enter to continue..." +} + +show_log_location() { + local log_file + log_file="$(get_log_file)" + + echo + echo "Log file location:" + echo "$log_file" + echo + read -rp "Press Enter to continue..." +} + +show_menu() { + clear + echo "==================================" + echo " Update Manager UI" + echo "==================================" + echo + echo "1) Run update check" + echo "2) View full log" + echo "3) Follow log live" + echo "4) Show log location" + echo "0) Exit" + echo +} + +main() { + if [[ ! -f "$UPDATE_MANAGER_SCRIPT" ]]; then + echo "Update manager script not found: $UPDATE_MANAGER_SCRIPT" + exit 1 + fi + + while true; do + show_menu + read -rp "Choose an option: " choice + + case "$choice" in + 1) + run_check + ;; + 2) + view_log + ;; + 3) + follow_log + ;; + 4) + show_log_location + ;; + 0) + echo + echo "Bye." + exit 0 + ;; + *) + echo + echo "Invalid choice." + read -rp "Press Enter to continue..." + ;; + esac + done +} + +main diff --git a/update-manager-ui.png b/update-manager-ui.png deleted file mode 100644 index 33929c8..0000000 Binary files a/update-manager-ui.png and /dev/null differ diff --git a/update-manager-ui.sh b/update-manager-ui.sh new file mode 100755 index 0000000..af17391 --- /dev/null +++ b/update-manager-ui.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash + +export DIALOGRC="/opt/update-manager/dialogrc" + +UPDATE_MANAGER_SCRIPT="$HOME/update-manager/update-manager.sh" +HOSTS_FILE="/opt/update-manager/hosts.conf" + +get_log_file() { + local primary="/opt/update-manager/log/update-manager.log" + local fallback="$HOME/update-manager/log/update-manager.log" + + if [[ -f "$primary" ]]; then + echo "$primary" + elif [[ -f "$fallback" ]]; then + echo "$fallback" + else + echo "" + fi +} + +show_log_menu() { + while true; do + log_choice=$(dialog --clear \ + --backtitle "Update Manager" \ + --title "Log menu" \ + --menu "Select log action:" 18 60 10 \ + 1 "View full log" \ + 2 "Follow log live" \ + 3 "Show log location" \ + 4 "Show last 20 log lines" \ + 0 "Back" \ + 3>&1 1>&2 2>&3) + + clear + + case "$log_choice" in + 1) + log_file="$(get_log_file)" + if [[ -n "$log_file" ]]; then + less "$log_file" + else + echo "No log file found yet." + read -rp "Press Enter to continue..." + fi + ;; + 2) + log_file="$(get_log_file)" + if [[ -n "$log_file" ]]; then + echo "Press Ctrl+C to stop" + tail -n 20 -f "$log_file" + else + echo "No log file found yet." + fi + read -rp "Press Enter to continue..." + ;; + 3) + log_file="$(get_log_file)" + if [[ -n "$log_file" ]]; then + echo "Log file:" + echo "$log_file" + else + echo "No log file found yet." + fi + read -rp "Press Enter to continue..." + ;; + 4) + log_file="$(get_log_file)" + if [[ -n "$log_file" ]]; then + tail -n 20 "$log_file" + else + echo "No log file found yet." + fi + read -rp "Press Enter to continue..." + ;; + 0|"") + break + ;; + esac + done +} + +while true; do + choice=$(dialog --clear \ + --backtitle "Update Manager" \ + --title "Choose an action" \ + --menu "Select option:" 20 60 10 \ + 1 "Check all hosts" \ + 2 "View hosts file" \ + 3 "Edit hosts file" \ + 4 "Add host" \ + 5 "Remove host" \ + 6 "Log menu" \ + 0 "Exit" \ + 3>&1 1>&2 2>&3) + + clear + + case "$choice" in + 1) + bash "$UPDATE_MANAGER_SCRIPT" check + read -rp "Press Enter to continue..." + ;; + 2) + less "$HOSTS_FILE" + ;; + 3) + nano "$HOSTS_FILE" + ;; + 4) + read -rp "Name: " name + read -rp "IP: " ip + read -rp "User: " user + if [[ -z "$name" || -z "$ip" || -z "$user" ]]; then + echo "All fields are required." + else + echo "$name $ip $user" >> "$HOSTS_FILE" + echo "Host added." + fi + read -rp "Press Enter to continue..." + ;; + 5) + nl -w2 -s'. ' "$HOSTS_FILE" + read -rp "Line to remove: " line + if [[ "$line" =~ ^[0-9]+$ ]]; then + sed -i "${line}d" "$HOSTS_FILE" + echo "Host removed." + else + echo "Invalid line number." + fi + read -rp "Press Enter to continue..." + ;; + 6) + show_log_menu + ;; + 0|"") + clear + exit 0 + ;; + esac +done diff --git a/update-manager.sh b/update-manager.sh index 89b5ef9..679fea0 100755 --- a/update-manager.sh +++ b/update-manager.sh @@ -1,127 +1,79 @@ #!/usr/bin/env bash -CONFIG_FILE="/etc/update-manager.conf" -[[ -f "$CONFIG_FILE" ]] || CONFIG_FILE="./update-manager.conf" +UPDATE_MANAGER_SCRIPT="$HOME/update-manager/update-manager.sh" +HOSTS_FILE="/opt/update-manager/hosts.conf" -if [[ -f "$CONFIG_FILE" ]]; then - # shellcheck disable=SC1090 - source "$CONFIG_FILE" -fi +get_log_file() { + local primary="/opt/update-manager/log/update-manager.log" + local fallback="$HOME/update-manager/log/update-manager.log" -DEFAULT_HOSTS_FILE="${HOSTS_FILE:-/opt/update-manager/hosts.conf}" -SSH_OPTIONS="${SSH_OPTIONS:--o BatchMode=yes -o ConnectTimeout=5}" - -######################################## -# Ensure hosts file exists -######################################## - -ensure_hosts_file() { - if [[ ! -f "$DEFAULT_HOSTS_FILE" ]]; then - echo "Creating example hosts file: $DEFAULT_HOSTS_FILE" - - mkdir -p "$(dirname "$DEFAULT_HOSTS_FILE")" - - cat > "$DEFAULT_HOSTS_FILE" <<'EOF' -# Example hosts file -# Format: -# name ip user -# -# server1 192.168.1.10 user -# server2 192.168.1.20 user -# server3 10.0.0.5 root -EOF - fi -} - -######################################## -# Helpers -######################################## - -get_hosts_file() { - if [[ -n "$2" ]]; then - echo "$2" + if [[ -f "$primary" ]]; then + echo "$primary" else - echo "$DEFAULT_HOSTS_FILE" + echo "$fallback" fi } -usage() { - cat <&1 1>&2 2>&3) -Examples: - $0 check - $0 check /path/to/hosts.conf - HOSTS_FILE=/path/to/hosts.conf $0 check -USAGE -} + clear -######################################## -# Check single host -######################################## - -check_host() { - local name="$1" - local ip="$2" - local user="$3" - local result - local rc - - result=$(ssh -n $SSH_OPTIONS "${user}@${ip}" \ - "apt list --upgradable 2>/dev/null | sed '/^Listing/d'" 2>&1) - rc=$? - - printf "===== %s (%s) =====\n" "$name" "$ip" - - if [[ $rc -ne 0 ]]; then - echo "❌ connection failed" - echo "$result" - echo - return - fi - - if [[ -z "$result" ]]; then - echo "Up-to-date" - else - echo "$result" - fi - - echo -} - -######################################## -# Check all hosts -######################################## - -check_all() { - local hosts_file="$1" - - ensure_hosts_file - - while IFS= read -r line; do - [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue - - local name ip user - read -r name ip user <<< "$line" - - [[ -z "$name" || -z "$ip" || -z "$user" ]] && continue - - check_host "$name" "$ip" "$user" - done < "$hosts_file" -} - -######################################## -# Main -######################################## - -case "${1:-}" in - check) - HOSTS_FILE_TO_USE="$(get_hosts_file "$@")" - check_all "$HOSTS_FILE_TO_USE" - ;; - *) - usage - exit 1 - ;; -esac + case $choice in + 1) + bash "$UPDATE_MANAGER_SCRIPT" check + read -rp "Press Enter to continue..." + ;; + 2) + less "$HOSTS_FILE" + ;; + 3) + nano "$HOSTS_FILE" + ;; + 4) + read -rp "Name: " name + read -rp "IP: " ip + read -rp "User: " user + echo "$name $ip $user" >> "$HOSTS_FILE" + echo "Host added." + read -rp "Press Enter to continue..." + ;; + 5) + nl -w2 -s'. ' "$HOSTS_FILE" + read -rp "Line to remove: " line + sed -i "${line}d" "$HOSTS_FILE" + echo "Host removed." + read -rp "Press Enter to continue..." + ;; + 6) + less "$(get_log_file)" + ;; + 7) + echo "Press Ctrl+C to stop" + tail -f "$(get_log_file)" + read -rp "Press Enter to continue..." + ;; + 8) + echo "Log file:" + echo "$(get_log_file)" + read -rp "Press Enter to continue..." + ;; + 0) + clear + exit 0 + ;; + esac +done