diff --git a/LOGOASCI.TXT b/LOGOASCI.TXT new file mode 100644 index 0000000..61b310a --- /dev/null +++ b/LOGOASCI.TXT @@ -0,0 +1,27 @@ + ██████ ██████ █████ █████ ████ ██████ █████████ ███████ ████████ +░░██████ ██████ ███░░░███░░████ ░░██ ███░░░███░█░░░██░░█░░██░░░█░░███░░███ + ░███░█████░███ ███ ░░███░██░██ ░██ ░███ ░░░ ░ ░██ ░ ░██ █ ░███ ░███ + ░███░░███ ░███░███ ░███░██ ░██░██ ░░███████ ░██ ░████ ░██████ + ░███ ░░░ ░███░███ ░███░██ ░████ ░░░░███ ░██ ░██░█ ░███░░███ + ░███ ░███░░███ ███ ░███ ░███ ███ ░███ ░██ ░██ █ ░███ ░███ + █████ █████░░░█████░ ████ ░████░░███████ █████ ███████ ████ ████ +░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░ ░░░░░░░ ░░░░ ░░░░░░ ░░░░ ░░░░ + ███████████ ███████ ███████████ ███████ ███████████ +░░███░░░░░███ ███░░░░░███░░███░░░░░███ ███░░░░░███ ░█░░░███░░░█ + ░███ ░███ ███ ░░███░███ ░███ ███ ░░███░ ░███ ░ + ░██████████ ░███ ░███░██████████ ░███ ░███ ░███ + ░███░░░░░███░███ ░███░███░░░░░███░███ ░███ ░███ + ░███ ░███░░███ ███ ░███ ░███░░███ ███ ░███ + █████ █████░░░███████░ ███████████ ░░░███████░ █████ +░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░░ ░░░░░░░ ░░░░░ + █████████ ███████ ███████████ ███████████ + ███░░░░░███ ███░░░░░███░░███░░░░░░█░█░░░███░░░█ +░███ ░░░ ███ ░░███░███ █ ░ ░ ░███ ░ +░░█████████ ░███ ░███░███████ ░███ + ░░░░░░░░███░███ ░███░███░░░█ ░███ + ███ ░███░░███ ███ ░███ ░ ░███ +░░█████████ ░░░███████░ █████ █████ + ░░░░░░░░░ ░░░░░░░ ░░░░░ ░░░░░ + + + \ No newline at end of file diff --git a/README.md b/README.md index b8c5eda..5292cb1 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,19 @@ UltraBunt is the **ULTIMATE** Ubuntu setup script that transforms your fresh Ubu Think of it as a whole bunch of blu-tac'd together bash scripts that: - 🧙‍♂️ Install up to 560+ essential applications - 🎪 Organized into 25+ magnificent categories -- 🚀 Handles APT, Snap, Flatpak, and custom installations +- 🚀 Handles `apt`, `snap`, `flatpak`, and `.deb` installers - 🎨 Makes your terminal look absolutely gorgeous - 🔒 Set up a native LAMP wordpress site that works without all the ball-ache - ☕ Saves you pissfarting around in terminal for hours every frikkin time you install it +## ✅ Install Policy + +UltraBunt installs apps only via `apt`, `snap`, `.deb`, or `flatpak` on Ubuntu-based systems. + +- Python `pip` installations are disabled and have been removed. +- Pip-only packages have been removed from the script and from this README. +- Where possible, equivalents using `apt`, `snap`, `.deb`, or `flatpak` are provided. + ## 🎪 THE MAGNIFICENT BUNTAGE CATEGORIES 🎪 ### 🔧 Core Utilities - The Foundation of Greatness @@ -50,7 +58,7 @@ Programming languages, frameworks, and development utilities: **Python3** - Python interpreter [APT] **Python3-pip** - Python package manager [APT] **Pipx** - Python app installer [APT] -**Poetry** - Python dependency manager [SCRIPT] +**Poetry** - Python dependency manager [APT] **Pyenv** - Python version manager [SCRIPT] **Go** - Go programming language [APT] **Rust** - Rust programming language [SCRIPT] @@ -80,9 +88,9 @@ Programming languages, frameworks, and development utilities: **VirtualBox** - Virtualization [SCRIPT] **QEMU** - Machine emulator [APT] **Neovim** - Modern Vim editor [APT] -**MkDocs** - Documentation generator [SCRIPT] -**Insomnia** - API testing tool [SCRIPT] -**Zed** - High-performance code editor [SCRIPT] +**MkDocs** - Documentation generator [APT] +**Insomnia** - API testing tool [DEB] +**Zed** - High-performance code editor [DEB] **Postman** - API development platform [SCRIPT] **Bruno** - Open-source API client [SCRIPT] **Bruno (Snap)** - Bruno via Snap [SNAP] @@ -560,9 +568,8 @@ Graphics editing and design applications: **GIMP (Flatpak)** - GIMP via Flatpak [FLATPAK] **Inkscape (Snap)** - Inkscape via Snap [SNAP] **Inkscape (Flatpak)** - Inkscape via Flatpak [FLATPAK] -**Webcamize** - Webcam utility [SCRIPT] -**Durdraw** - ASCII art editor [SCRIPT] -**Pastel** - Color manipulation tool [SCRIPT] +**Webcamize** - Webcam utility [APT] +**Pastel** - Color manipulation tool [APT] ### 📄 Office & Productivity - Get Things Done Office suites and productivity applications: @@ -585,22 +592,10 @@ Cutting-edge AI and machine learning tools: **Ollama** - Run LLMs locally [SCRIPT] **Gollama** - Ollama GUI client [SCRIPT] **LM Studio** - Local LLM interface [SCRIPT] -**Text Generation WebUI** - Local text generation [SCRIPT] +**Ollama** - Run LLMs locally [SCRIPT] +**Gollama** - Ollama GUI client [SCRIPT] +**LM Studio** - Local LLM interface [SCRIPT] **Whisper-cpp** - Speech recognition [SCRIPT] -**ComfyUI** - Node-based AI interface [SCRIPT] -**InvokeAI** - Professional AI art [SCRIPT] -**LMDeploy** - LLM deployment toolkit [SCRIPT] -**KoboldCpp** - AI storytelling backend [SCRIPT] -**Automatic1111** - Popular SD interface [SCRIPT] -**Fooocus** - Simplified Stable Diffusion [SCRIPT] -**SD-Next** - Advanced Stable Diffusion [SCRIPT] -**Kohya SS GUI** - LoRA training interface [SCRIPT] -**Faster-Whisper** - Optimized speech recognition [SCRIPT] -**WhisperX** - Enhanced Whisper with alignment [SCRIPT] -**Coqui-STT** - Speech-to-text engine [SCRIPT] -**Piper-TTS** - Text-to-speech synthesis [SCRIPT] -**Mimic3** - Neural text-to-speech [SCRIPT] -**Coqui-TTS** - Advanced text-to-speech [SCRIPT] **Yai** - AI powered terminal assistant [CUSTOM] ### 🕹️ Gaming Emulators - Retro Gaming diff --git a/instructions-for-claude.txt b/instructions-for-claude.txt new file mode 100644 index 0000000..9343f86 --- /dev/null +++ b/instructions-for-claude.txt @@ -0,0 +1,218 @@ + +Goal: +Expand an Ubuntu bash installer script so that it configures Cloudflare Firewall rules automatically using the user’s Zone ID and API Token. +The script should support three security levels — Light, Moderate, and Extreme — all using Cloudflare’s free-tier Firewall Rules API. + +⸻ + +🪟 User Inputs (interactive prompt in terminal) + 1. CLOUDFLARE_EMAIL (optional, only for logging) + 2. ZONE_ID + 3. API_TOKEN + 4. DOMAIN_NAME (used for referer-safe rules) + 5. SECURITY_LEVEL (choose: light, moderate, or extreme) + +⸻ + +🧰 Dependencies (for a clean Ubuntu install) + +sudo apt update +sudo apt install -y curl jq + +(jq is for parsing JSON responses and checking rule creation success.) + +⸻ + +🌩️ Cloudflare API Base Endpoint + +https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/firewall/rules + +Headers: + +-H "Authorization: Bearer ${API_TOKEN}" \ +-H "Content-Type: application/json" + + +⸻ + +🪄 Security Levels + +🟢 LIGHT (basic protection, no admin lockdown) + • Block /xmlrpc.php + • Block /wp-content/ or /wp-includes/ requests not from your domain + +Expression: + +(http.request.uri.path contains "/xmlrpc.php") or +((http.request.uri.path eq "/wp-content/" or http.request.uri.path eq "/wp-includes/") and not http.referer contains "${DOMAIN_NAME}") + + +⸻ + +🟡 MODERATE (adds wp-admin/wp-login limits) +Includes Light rules + +Block access to /wp-login.php or /wp-admin/ (except /wp-admin/admin-ajax.php) from outside AU, US, UK. + +Expression: + +( + http.request.uri.path contains "/wp-login.php" or + (http.request.uri.path contains "/wp-admin/" and http.request.uri.path ne "/wp-admin/admin-ajax.php") +) +and not (ip.geoip.country in {"AU" "US" "UK"}) + + +⸻ + +🔴 EXTREME (maximum free-tier hardening) +Includes Moderate rules + +Block entire site access from sketchy regions (RU, CN, KP, IR, etc.), except known search engine bots (Googlebot, Bingbot). + +Expression: + +( + ip.geoip.country in {"RU" "CN" "KP" "IR" "VN" "TR" "IN" "NG"} +) +and not cf.client.bot + + +⸻ + +🧩 Example Bash Logic + +Here’s the conceptual structure for your installer: + +read -p "Enter your Cloudflare Zone ID: " ZONE_ID +read -p "Enter your Cloudflare API Token: " API_TOKEN +read -p "Enter your domain (e.g. example.com): " DOMAIN +read -p "Choose security level (light/moderate/extreme): " LEVEL + +API="https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/firewall/rules" + +create_rule() { + local desc="$1" + local expr="$2" + echo "Creating rule: $desc..." + curl -s -X POST "$API" \ + -H "Authorization: Bearer ${API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data "[ + { + \"description\": \"${desc}\", + \"action\": \"block\", + \"filter\": {\"expression\": \"${expr}\"} + } + ]" | jq . +} + +if [[ "$LEVEL" == "light" || "$LEVEL" == "moderate" || "$LEVEL" == "extreme" ]]; then + create_rule "Block XMLRPC and unauthorized includes" "(http.request.uri.path contains \"/xmlrpc.php\") or ((http.request.uri.path eq \"/wp-content/\" or http.request.uri.path eq \"/wp-includes/\") and not http.referer contains \"${DOMAIN}\")" +fi + +if [[ "$LEVEL" == "moderate" || "$LEVEL" == "extreme" ]]; then + create_rule "Restrict wp-login/wp-admin to AU, US, UK" "((http.request.uri.path contains \"/wp-login.php\" or (http.request.uri.path contains \"/wp-admin/\" and http.request.uri.path ne \"/wp-admin/admin-ajax.php\")) and not (ip.geoip.country in {\"AU\" \"US\" \"UK\"}))" +fi + +if [[ "$LEVEL" == "extreme" ]]; then + create_rule "Block access from high-risk countries" "((ip.geoip.country in {\"RU\" \"CN\" \"KP\" \"IR\" \"VN\" \"TR\" \"IN\" \"NG\"}) and not cf.client.bot)" +fi + +echo "✅ Cloudflare security rules applied for ${DOMAIN} (${LEVEL} mode)" + + +extra concept to factor in... + + +💀 “Mega Bot Block” Expansion for Cloudflare Setup Script + +This will: + • Keep Googlebot, Bingbot, DuckDuckGo, Applebot ✅ allowed. + • Block AI scrapers, SEO bots, curl/wget libraries, and other nuisance crawlers. + • Skip blocking /robots.txt so legitimate search engines can still fetch it. + • Use the same ZONE_ID and API_TOKEN you already input earlier. + +⸻ + +⚙️ Integration Plan + +In your Cloudflare bash installer (from before), add a 4th rule group triggered if user enables “bot blocking.” +At the prompt, include: + +read -p "Enable advanced bot blocking? (y/n): " BOTBLOCK + +If y, it will add a rule called: + +“AI Crawl Control - Block AI & SEO Bots” + +⸻ + +💬 Expression (copy-ready for API) + +(http.request.uri.path ne "/robots.txt") and ( + (http.user_agent contains "SemrushBot") or + (http.user_agent contains "AhrefsBot") or + (http.user_agent contains "Barkrowler") or + (http.user_agent contains "MJ12bot") or + (http.user_agent contains "DotBot") or + (http.user_agent contains "Exabot") or + (http.user_agent contains "Sogou") or + (http.user_agent contains "YandexBot") or + (http.user_agent contains "Baidu") or + (http.user_agent contains "ia_archiver") or + (http.user_agent contains "SemanticaBot") or + (http.user_agent contains "SiteLockSpider") or + (http.user_agent contains "SeznamBot") or + (http.user_agent contains "OpenLinkProfiler") or + (http.user_agent contains "SiteBot") or + (http.user_agent contains "Screaming Frog") or + (http.user_agent contains "DataForSeoBot") or + (http.user_agent contains "SEOkicks") or + (http.user_agent contains "BLEXBot") or + (http.user_agent contains "MegaIndex") or + (http.user_agent contains "MegaIndex.ru") or + (http.user_agent contains "NetcraftSurveyAgent") or + (http.user_agent contains "Apache-HttpClient") or + (http.user_agent contains "Python-urllib") or + (http.user_agent contains "python-requests") or + (http.user_agent contains "libwww-perl") or + (http.user_agent contains "Curl") or + (http.user_agent contains "wget") or + (http.user_agent contains "CensysInspect") or + (http.user_agent contains "ZoominfoBot") or + (http.user_agent contains "MauiBot") or + (http.user_agent contains "spbot") or + (http.user_agent contains "GPTBot") or + (http.user_agent contains "ClaudeBot") or + (http.user_agent contains "Claude-SearchBot") or + (http.user_agent contains "Claude-User") or + (http.user_agent contains "Bytespider") or + (http.user_agent contains "MistralAI-User") or + (http.user_agent contains "Perplexity-User") or + (http.user_agent contains "ProRataInc") or + (http.user_agent contains "Novellum") or + (http.user_agent contains "OAI-SearchBot") or + (http.user_agent contains "Meta-ExternalFetcher") or + (http.user_agent contains "Meta-ExternalAgent") +) + + +⸻ + +🧠 Add this function into the script + +if [[ "$BOTBLOCK" =~ ^[Yy]$ ]]; then + create_rule "AI Crawl Control - Block AI & SEO Bots" \ + '(http.request.uri.path ne "/robots.txt") and ((http.user_agent contains "SemrushBot") or (http.user_agent contains "AhrefsBot") or (http.user_agent contains "Barkrowler") or (http.user_agent contains "MJ12bot") or (http.user_agent contains "DotBot") or (http.user_agent contains "Exabot") or (http.user_agent contains "Sogou") or (http.user_agent contains "YandexBot") or (http.user_agent contains "Baidu") or (http.user_agent contains "ia_archiver") or (http.user_agent contains "SemanticaBot") or (http.user_agent contains "SiteLockSpider") or (http.user_agent contains "SeznamBot") or (http.user_agent contains "OpenLinkProfiler") or (http.user_agent contains "SiteBot") or (http.user_agent contains "Screaming Frog") or (http.user_agent contains "DataForSeoBot") or (http.user_agent contains "SEOkicks") or (http.user_agent contains "BLEXBot") or (http.user_agent contains "MegaIndex") or (http.user_agent contains "MegaIndex.ru") or (http.user_agent contains "NetcraftSurveyAgent") or (http.user_agent contains "Apache-HttpClient") or (http.user_agent contains "Python-urllib") or (http.user_agent contains "python-requests") or (http.user_agent contains "libwww-perl") or (http.user_agent contains "Curl") or (http.user_agent contains "wget") or (http.user_agent contains "CensysInspect") or (http.user_agent contains "ZoominfoBot") or (http.user_agent contains "MauiBot") or (http.user_agent contains "spbot") or (http.user_agent contains "GPTBot") or (http.user_agent contains "ClaudeBot") or (http.user_agent contains "Claude-SearchBot") or (http.user_agent contains "Claude-User") or (http.user_agent contains "Bytespider") or (http.user_agent contains "MistralAI-User") or (http.user_agent contains "Perplexity-User") or (http.user_agent contains "ProRataInc") or (http.user_agent contains "Novellum") or (http.user_agent contains "OAI-SearchBot") or (http.user_agent contains "Meta-ExternalFetcher") or (http.user_agent contains "Meta-ExternalAgent"))' +fi + + +⸻ + +✅ Summary of What This Adds + +Rule Type Action Notes +/xmlrpc.php & unauthorized includes Block Basic protection +wp-login & wp-admin by region Block Optional Moderate/Extreme +Country blocks (RU, CN, KP, etc.) Block Optional Extreme +Bot/AI/SEO crawler block Block Optional, keeps search engines allowed + diff --git a/linux-terminal-diagnostics-output.txt b/linux-terminal-diagnostics-output.txt new file mode 100644 index 0000000..19c6217 --- /dev/null +++ b/linux-terminal-diagnostics-output.txt @@ -0,0 +1,1469 @@ +staller exiting. +lscpu +Architecture: x86_64 + CPU op-mode(s): 32-bit, 64-bit + Address sizes: 39 bits physical, 48 bits virtual + Byte Order: Little Endian +CPU(s): 4 + On-line CPU(s) list: 0-3 +Vendor ID: GenuineIntel + Model name: Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz + CPU family: 6 + Model: 69 + Thread(s) per core: 2 + Core(s) per socket: 2 + Socket(s): 1 + Stepping: 1 + CPU(s) scaling MHz: 62% + CPU max MHz: 2700.0000 + CPU min MHz: 800.0000 + BogoMIPS: 4000.11 + Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb + rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx + est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpu + id_fault epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveop + t dtherm ida arat pln pts vnmi md_clear flush_l1d +Virtualization features: + Virtualization: VT-x +Caches (sum of all): + L1d: 64 KiB (2 instances) + L1i: 64 KiB (2 instances) + L2: 512 KiB (2 instances) + L3: 3 MiB (1 instance) +NUMA: + NUMA node(s): 1 + NUMA node0 CPU(s): 0-3 +Vulnerabilities: + Gather data sampling: Not affected + Ghostwrite: Not affected + Indirect target selection: Not affected + Itlb multihit: KVM: Mitigation: Split huge pages + L1tf: Mitigation; PTE Inversion; VMX conditional cache flushes, SMT vulnerable + Mds: Mitigation; Clear CPU buffers; SMT vulnerable + Meltdown: Mitigation; PTI + Mmio stale data: Unknown: No mitigations + Reg file data sampling: Not affected + Retbleed: Not affected + Spec rstack overflow: Not affected + Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl + Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization + Spectre v2: Mitigation; Retpolines; IBPB conditional; IBRS_FW; STIBP conditional; RSB filling; PBRSB-eIBRS Not affected; BHI Not affected + Srbds: Mitigation; Microcode + Tsx async abort: Not affected +cat /proc/cpuinfo +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 69 +model name : Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz +stepping : 1 +microcode : 0x26 +cpu MHz : 2160.458 +cache size : 3072 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_unknown +bogomips : 4000.11 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 69 +model name : Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz +stepping : 1 +microcode : 0x26 +cpu MHz : 2101.815 +cache size : 3072 KB +physical id : 0 +siblings : 4 +core id : 1 +cpu cores : 2 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_unknown +bogomips : 4000.11 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 69 +model name : Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz +stepping : 1 +microcode : 0x26 +cpu MHz : 2182.793 +cache size : 3072 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 2 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_unknown +bogomips : 4000.11 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 3 +vendor_id : GenuineIntel +cpu family : 6 +model : 69 +model name : Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz +stepping : 1 +microcode : 0x26 +cpu MHz : 2139.493 +cache size : 3072 KB +physical id : 0 +siblings : 4 +core id : 1 +cpu cores : 2 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_unknown +bogomips : 4000.11 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +free -h + total used free shared buff/cache available +Mem: 3.8Gi 2.3Gi 282Mi 259Mi 1.7Gi 1.5Gi +Swap: 3.8Gi 1.7Gi 2.1Gi +sudo dmidecode - - type memory +[sudo] password for ronster: +# dmidecode 3.5 +Getting SMBIOS data from sysfs. +SMBIOS 2.4 present. +43 structures occupying 2616 bytes. +Table at 0x8CD10000. + +Wrong DMI structures length: 2616 bytes announced, only 2205 bytes available. +Handle 0x0000, DMI type 4, 35 bytes +Processor Information + Socket Designation: U3E1 + Type: Central Processor + Family: Core i5 + Manufacturer: Intel(R) Corporation + ID: 51 06 04 00 FF FB EB BF + Signature: Type 0, Family 6, Model 69, Stepping 1 + Flags: + FPU (Floating-point unit on-chip) + VME (Virtual mode extension) + DE (Debugging extension) + PSE (Page size extension) + TSC (Time stamp counter) + MSR (Model specific registers) + PAE (Physical address extension) + MCE (Machine check exception) + CX8 (CMPXCHG8 instruction supported) + APIC (On-chip APIC hardware supported) + SEP (Fast system call) + MTRR (Memory type range registers) + PGE (Page global enable) + MCA (Machine check architecture) + CMOV (Conditional move instruction supported) + PAT (Page attribute table) + PSE-36 (36-bit page size extension) + CLFSH (CLFLUSH instruction supported) + DS (Debug store) + ACPI (ACPI supported) + MMX (MMX technology supported) + FXSR (FXSAVE and FXSTOR instructions supported) + SSE (Streaming SIMD extensions) + SSE2 (Streaming SIMD extensions 2) + SS (Self-snoop) + HTT (Multi-threading) + TM (Thermal monitor supported) + PBE (Pending break enabled) + Version: Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz + Voltage: 0.9 V + External Clock: 25 MHz + Max Speed: 1400 MHz + Current Speed: 1400 MHz + Status: Populated, Enabled + Upgrade: Socket BGA1168 + L1 Cache Handle: 0x0002 + L2 Cache Handle: 0x0003 + L3 Cache Handle: 0x0004 + Serial Number: To Be Filled By O.E.M. + Asset Tag: To Be Filled By O.E.M. + Part Number: Not Specified + +Handle 0x0001, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 1 + Operational Mode: Write Back + Location: Internal + Installed Size: 64 kB + Maximum Size: 64 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Data + Associativity: 8-way Set-associative + +Handle 0x0002, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 1 + Operational Mode: Write Back + Location: Internal + Installed Size: 64 kB + Maximum Size: 64 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Instruction + Associativity: 8-way Set-associative + +Handle 0x0003, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 2 + Operational Mode: Write Back + Location: Internal + Installed Size: 512 kB + Maximum Size: 512 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Unified + Associativity: 8-way Set-associative + +Handle 0x0004, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 3 + Operational Mode: Write Back + Location: Internal + Installed Size: 3 MB + Maximum Size: 3 MB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Unified + Associativity: 12-way Set-associative + +Handle 0x0005, DMI type 16, 15 bytes +Physical Memory Array + Location: System Board Or Motherboard + Use: System Memory + Error Correction Type: None + Maximum Capacity: 8 GB + Error Information Handle: Not Provided + Number Of Devices: 2 + +Handle 0x0006, DMI type 130, 186 bytes +OEM-specific Type + Header and Data: + 82 BA 06 00 07 00 00 00 B0 00 92 10 F1 03 04 11 + 05 0B 03 11 01 08 0A 00 F4 01 78 78 90 50 90 11 + 50 E0 10 04 3C 3C 01 90 83 8C 00 A1 00 00 00 00 + 00 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 + FE 00 00 00 00 00 00 00 AF E6 45 42 4A 32 30 55 + 46 38 45 44 55 30 2D 47 4E 2D 46 20 30 20 02 FE + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 + +Handle 0x0007, DMI type 17, 34 bytes +Memory Device + Array Handle: 0x0005 + Error Information Handle: No Error + Total Width: Unknown + Data Width: Unknown + Size: 2 GB + Form Factor: SODIMM + Set: None + Locator: DIMM0 + Bank Locator: BANK 0 + Type: DDR3 + Type Detail: Synchronous + Speed: 1600 MT/s + Manufacturer: 0x02FE + Serial Number: 0x00000000 + Asset Tag: Unknown + Part Number: 0x45424A3230554638454455302D474E2D4620 + Rank: Unknown + Configured Memory Speed: Unknown + +Handle 0x0008, DMI type 130, 186 bytes +OEM-specific Type + Header and Data: + 82 BA 08 00 09 00 00 00 B0 00 92 10 F1 03 04 11 + 05 0B 03 11 01 08 0A 00 F4 01 78 78 90 50 90 11 + 50 E0 10 04 3C 3C 01 90 83 8C 00 A1 00 00 00 00 + 00 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 + FE 00 00 00 00 00 00 00 AF E6 45 42 4A 32 30 55 + 46 38 45 44 55 30 2D 47 4E 2D 46 20 30 20 02 FE + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 + +Handle 0x0009, DMI type 17, 34 bytes +Memory Device + Array Handle: 0x0005 + Error Information Handle: No Error + Total Width: Unknown + Data Width: Unknown + Size: 2 GB + Form Factor: SODIMM + Set: None + Locator: DIMM0 + Bank Locator: BANK 1 + Type: DDR3 + Type Detail: Synchronous + Speed: 1600 MT/s + Manufacturer: 0x02FE + Serial Number: 0x00000000 + Asset Tag: Unknown + Part Number: 0x45424A3230554638454455302D474E2D4620 + Rank: Unknown + Configured Memory Speed: Unknown + +Handle 0x000A, DMI type 19, 15 bytes +Memory Array Mapped Address + Starting Address: 0x00000000000 + Ending Address: 0x000FFFFFFFF + Range Size: 4 GB + Physical Array Handle: 0x0005 + Partition Width: 0 + +Handle 0x000B, DMI type 0, 24 bytes +BIOS Information + Vendor: Apple Inc. + Version: 431.140.6.0.0 + Release Date: 06/13/2021 + ROM Size: 8 MB + Characteristics: + PCI is supported + BIOS is upgradeable + BIOS shadowing is allowed + Boot from CD is supported + Selectable boot is supported + ACPI is supported + IEEE 1394 boot is supported + Smart battery is supported + Function key-initiated network boot is supported + BIOS Revision: 0.1 + +Handle 0x000C, DMI type 1, 27 bytes +System Information + Manufacturer: Apple Inc. + Product Name: MacBookAir6,2 + Version: 1.0 + Serial Number: C02NJSN2G085 + UUID: 46f5f24d-5e97-d651-876c-174121a74ad3 + Wake-up Type: Power Switch + SKU Number: System SKU# + Family: MacBook Air + +Handle 0x000D, DMI type 2, 16 bytes +Base Board Information + Manufacturer: Apple Inc. + Product Name: Mac-7DF21CB3ED6977E5 + Version: MacBookAir6,2 + Serial Number: C0244060HPWFY58AQ + Asset Tag: Base Board Asset Tag# + Features: + Board is a hosting board + Board is replaceable + Location In Chassis: Part Component + Chassis Handle: 0x000E + Type: Motherboard + Contained Object Handles: 0 + +Handle 0x000E, DMI type 3, 21 bytes +Chassis Information + Manufacturer: Apple Inc. + Type: Notebook + Lock: Not Present + Version: Mac-7DF21CB3ED6977E5 + Serial Number: C02NJSN2G085 + Asset Tag: Not Specified + Boot-up State: Safe + Power Supply State: Safe + Thermal State: Other + Security Status: Other + OEM Information: 0x00000000 + Height: Unspecified + Number Of Power Cords: Unspecified + Contained Elements: 0 + +Handle 0x000F, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: USB0 + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0010, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: USB1 + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0011, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Mini DisplayPort + External Connector Type: None + Port Type: Other + +Handle 0x0012, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: HDMI + External Connector Type: None + Port Type: Other + +Handle 0x0013, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Microphone + External Connector Type: None + Port Type: Other + +Handle 0x0014, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Speaker + External Connector Type: None + Port Type: Other + +Handle 0x0015, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: MagSafe DC Power + External Connector Type: Other + Port Type: Other + +Handle 0x0016, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: Audio Line Out + External Connector Type: Mini Jack (headphones) + Port Type: Audio Port + +Handle 0x0017, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: SD Card Reader + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0018, DMI type 9, 13 bytes +System Slot Information + Designation: AirPort + Type: x1 PCI Express + Current Usage: Available + Length: Short + ID: 0 + Characteristics: + 3.3 V is provided + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x0019, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 1 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 2 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001A, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 2 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 3 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001B, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 3 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 4 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001C, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 4 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 5 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001D, DMI type 10, 6 bytes +On Board Device Information + Type: Video + Status: Enabled + Description: Integrated Video Controller + +Handle 0x001E, DMI type 10, 6 bytes +On Board Device Information + Type: Sound + Status: Enabled + Description: Azalia Audio Codec + +Handle 0x001F, DMI type 10, 6 bytes +On Board Device Information + Type: Other + Status: Enabled + Description: SATA + +Handle 0x0020, DMI type 11, 5 bytes +OEM Strings + String 1: Apple ROM Version. Model: MBA61. EFI Version: 431.140 + String 2: .6.0.0. Built by: root@bb-g9-pdb92. Date: Sun Jun + String 3: 13 18:52:10 PDT 2021. Revision: 431.140.6 (B&I). ROM Ver + String 4: sion: F000_B00. Build Type: Official Build, Release. Compi + String 5: ler: clang-1205.0.19.59.6. + +Handle 0x0021, DMI type 12, 5 bytes +System Configuration Options + +Handle 0x0022, DMI type 13, 22 bytes +BIOS Language Information + Language Description Format: Long + Installable Languages: 1 + + Currently Installed Language: Not Specified + +Handle 0x0023, DMI type 32, 20 bytes +System Boot Information + Status: No errors detected + +Handle 0x0024, DMI type 131, 6 bytes +OEM-specific Type + Header and Data: + 83 06 24 00 05 06 + +Handle 0x0025, DMI type 128, 88 bytes +OEM-specific Type + Header and Data: + 80 58 25 00 03 00 00 00 77 F5 0F FB 7F FF 1F FF + 02 00 03 00 00 00 00 00 00 00 99 FF FF FF B2 FF + 00 00 B3 FF FF FF DD FF 00 00 E1 FF FF FF E3 FF + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + +Handle 0x0026, DMI type 133, 12 bytes +OEM-specific Type + Header and Data: + 85 0C 26 00 02 00 00 00 00 00 00 00 + +Handle 0x0027, DMI type 221, 26 bytes +OEM-specific Type + Header and Data: + DD 1A 27 00 03 01 00 01 08 00 00 00 02 00 00 00 + 00 26 00 03 00 01 03 00 01 00 + Strings: + Reference Code - CPU + uCode Version + TXT ACM version + +Wrong DMI structures count: 43 announced, only 40 decoded. +lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +loop0 7:0 0 4K 1 loop /snap/bare/5 +loop1 7:1 0 73.9M 1 loop /snap/core22/2133 +loop2 7:2 0 73.9M 1 loop /snap/core22/2139 +loop3 7:3 0 249.2M 1 loop /snap/firefox/7084 +loop4 7:4 0 66.8M 1 loop /snap/core24/1196 +loop5 7:5 0 66.8M 1 loop /snap/core24/1225 +loop6 7:6 0 245.1M 1 loop /snap/firefox/6565 +loop7 7:7 0 11.1M 1 loop /snap/firmware-updater/167 +loop8 7:8 0 18.5M 1 loop /snap/firmware-updater/210 +loop9 7:9 0 516.2M 1 loop /snap/gnome-42-2204/226 +loop10 7:10 0 516M 1 loop /snap/gnome-42-2204/202 +loop11 7:11 0 618.3M 1 loop /snap/gnome-46-2404/125 +loop12 7:12 0 91.7M 1 loop /snap/gtk-common-themes/1535 +loop13 7:13 0 17.1M 1 loop /snap/localsend/32 +loop14 7:14 0 302.8M 1 loop /snap/mesa-2404/1110 +loop15 7:15 0 290.8M 1 loop /snap/mesa-2404/912 +loop16 7:16 0 50.8M 1 loop /snap/snapd/25202 +loop17 7:17 0 10.8M 1 loop /snap/snap-store/1270 +loop18 7:18 0 50.9M 1 loop /snap/snapd/25577 +loop19 7:19 0 576K 1 loop /snap/snapd-desktop-integration/315 +loop20 7:20 0 8.1M 1 loop /snap/mirrorselect/6 +sda 8:0 0 113G 0 disk +├─sda1 8:1 0 1G 0 part /boot/efi +└─sda2 8:2 0 111.9G 0 part / +sudo fdisk -l +Disk /dev/loop0: 4 KiB, 4096 bytes, 8 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop1: 73.92 MiB, 77512704 bytes, 151392 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop2: 73.91 MiB, 77500416 bytes, 151368 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop3: 249.22 MiB, 261324800 bytes, 510400 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop4: 66.8 MiB, 70045696 bytes, 136808 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop5: 66.84 MiB, 70090752 bytes, 136896 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop6: 245.13 MiB, 257036288 bytes, 502024 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop7: 11.13 MiB, 11673600 bytes, 22800 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/sda: 113 GiB, 121332826112 bytes, 236978176 sectors +Disk model: APPLE SSD SM0128 +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 4096 bytes +I/O size (minimum/optimal): 4096 bytes / 4096 bytes +Disklabel type: gpt +Disk identifier: 896EBFA8-B4A7-4B9C-83E6-EE1AD1D0171D + +Device Start End Sectors Size Type +/dev/sda1 2048 2203647 2201600 1G EFI System +/dev/sda2 2203648 236976127 234772480 111.9G Linux filesystem + + +Disk /dev/loop8: 18.49 MiB, 19386368 bytes, 37864 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop10: 516.01 MiB, 541073408 bytes, 1056784 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop9: 516.2 MiB, 541278208 bytes, 1057184 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop11: 618.26 MiB, 648290304 bytes, 1266192 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop12: 91.69 MiB, 96141312 bytes, 187776 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop13: 17.13 MiB, 17965056 bytes, 35088 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop14: 302.77 MiB, 317480960 bytes, 620080 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop15: 290.77 MiB, 304898048 bytes, 595504 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop16: 50.77 MiB, 53235712 bytes, 103976 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop17: 10.83 MiB, 11354112 bytes, 22176 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop18: 50.93 MiB, 53399552 bytes, 104296 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop19: 576 KiB, 589824 bytes, 1152 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes + + +Disk /dev/loop20: 8.09 MiB, 8482816 bytes, 16568 sectors +Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes +df -h +Filesystem Size Used Avail Use% Mounted on +tmpfs 386M 2.3M 384M 1% /run +/dev/sda2 110G 28G 77G 27% / +tmpfs 1.9G 34M 1.9G 2% /dev/shm +tmpfs 5.0M 12K 5.0M 1% /run/lock +/dev/sda1 1.1G 6.2M 1.1G 1% /boot/efi +tmpfs 386M 164K 386M 1% /run/user/1000 +lspci -nnk +00:00.0 Host bridge [0600]: Intel Corporation Haswell-ULT DRAM Controller [8086:0a04] (rev 09) + Subsystem: Apple Inc. Haswell-ULT DRAM Controller [106b:011b] + Kernel driver in use: hsw_uncore +00:02.0 VGA compatible controller [0300]: Intel Corporation Haswell-ULT Integrated Graphics Controller [8086:0a26] (rev 09) + Subsystem: Apple Inc. Haswell-ULT Integrated Graphics Controller [106b:011b] + Kernel driver in use: i915 + Kernel modules: i915 +00:03.0 Audio device [0403]: Intel Corporation Haswell-ULT HD Audio Controller [8086:0a0c] (rev 09) + Subsystem: Apple Inc. Haswell-ULT HD Audio Controller [106b:011b] + Kernel driver in use: snd_hda_intel + Kernel modules: snd_hda_intel +00:14.0 USB controller [0c03]: Intel Corporation 8 Series USB xHCI HC [8086:9c31] (rev 04) + Subsystem: Intel Corporation Apple MacBookAir6,2 / MacBookPro11,1 [8086:7270] + Kernel driver in use: xhci_hcd +00:15.0 DMA controller [0801]: Intel Corporation 8 Series Low Power Sub-System DMA [8086:9c60] (rev 04) + Kernel driver in use: dw_dmac_pci + Kernel modules: dw_dmac_pci +00:15.4 Serial bus controller [0c80]: Intel Corporation 8 Series SPI Controller #1 [8086:9c66] (rev 04) + Kernel modules: spi_pxa2xx_pci +00:16.0 Communication controller [0780]: Intel Corporation 8 Series HECI #0 [8086:9c3a] (rev 04) + Subsystem: Intel Corporation 8 Series HECI [8086:7270] + Kernel driver in use: mei_me + Kernel modules: mei_me +00:1b.0 Audio device [0403]: Intel Corporation 8 Series HD Audio Controller [8086:9c20] (rev 04) + Subsystem: Intel Corporation 8 Series HD Audio Controller [8086:7270] + Kernel driver in use: snd_hda_intel + Kernel modules: snd_hda_intel +00:1c.0 PCI bridge [0604]: Intel Corporation 8 Series PCI Express Root Port 1 [8086:9c10] (rev e4) + Subsystem: Intel Corporation 8 Series PCI Express Root Port 1 [8086:7270] + Kernel driver in use: pcieport +00:1c.1 PCI bridge [0604]: Intel Corporation 8 Series PCI Express Root Port 2 [8086:9c12] (rev e4) + Subsystem: Intel Corporation 8 Series PCI Express Root Port 2 [8086:7270] + Kernel driver in use: pcieport +00:1c.2 PCI bridge [0604]: Intel Corporation 8 Series PCI Express Root Port 3 [8086:9c14] (rev e4) + Subsystem: Intel Corporation 8 Series PCI Express Root Port 3 [8086:7270] + Kernel driver in use: pcieport +00:1c.4 PCI bridge [0604]: Intel Corporation 8 Series PCI Express Root Port 5 [8086:9c18] (rev e4) + Subsystem: Intel Corporation 8 Series PCI Express Root Port 5 [8086:7270] + Kernel driver in use: pcieport +00:1c.5 PCI bridge [0604]: Intel Corporation 8 Series PCI Express Root Port 6 [8086:9c1a] (rev e4) + Subsystem: Intel Corporation 8 Series PCI Express Root Port 6 [8086:7270] + Kernel driver in use: pcieport +00:1f.0 ISA bridge [0601]: Intel Corporation 8 Series LPC Controller [8086:9c43] (rev 04) + Subsystem: Intel Corporation 8 Series LPC Controller [8086:7270] + Kernel driver in use: lpc_ich + Kernel modules: lpc_ich +00:1f.3 SMBus [0c05]: Intel Corporation 8 Series SMBus Controller [8086:9c22] (rev 04) + Subsystem: Intel Corporation 8 Series SMBus Controller [8086:7270] + Kernel driver in use: i801_smbus + Kernel modules: i2c_i801 +02:00.0 Multimedia controller [0480]: Broadcom Inc. and subsidiaries 720p FaceTime HD Camera [14e4:1570] + Subsystem: Broadcom Inc. and subsidiaries 720p FaceTime HD Camera [14e4:1570] +03:00.0 Network controller [0280]: Broadcom Inc. and subsidiaries BCM4360 802.11ac Dual Band Wireless Network Adapter [14e4:43a0] (rev 03) + Subsystem: Apple Inc. BCM4360 802.11ac Dual Band Wireless Network Adapter [106b:0117] + Kernel driver in use: wl + Kernel modules: bcma, wl +04:00.0 SATA controller [0106]: Samsung Electronics Co Ltd S4LN058A01[SSUBX] AHCI SSD Controller (Apple slot) [144d:a801] (rev 01) + Subsystem: Samsung Electronics Co Ltd S4LN058A01[SSUBX] AHCI SSD Controller (Apple slot) [144d:a801] + Kernel driver in use: ahci + Kernel modules: ahci +05:00.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +06:00.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +06:03.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +06:04.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +06:05.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +06:06.0 PCI bridge [0604]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: pcieport +07:00.0 System peripheral [0880]: Intel Corporation DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] [8086:1547] (rev 03) + Subsystem: Device [2222:1111] + Kernel driver in use: thunderbolt + Kernel modules: thunderbolt +lsusb -vt +/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/9p, 480M + ID 1d6b:0002 Linux Foundation 2.0 root hub + |__ Port 003: Dev 002, If 0, Class=Hub, Driver=hub/3p, 12M + ID 0a5c:4500 Broadcom Corp. BCM2046B1 USB 2.0 Hub (part of BCM2046 Bluetooth) + |__ Port 003: Dev 006, If 0, Class=Vendor Specific Class, Driver=btusb, 12M + ID 05ac:828f Apple, Inc. + |__ Port 003: Dev 006, If 1, Class=Wireless, Driver=btusb, 12M + ID 05ac:828f Apple, Inc. + |__ Port 003: Dev 006, If 2, Class=Vendor Specific Class, Driver=btusb, 12M + ID 05ac:828f Apple, Inc. + |__ Port 003: Dev 006, If 3, Class=Application Specific Interface, Driver=[none], 12M + ID 05ac:828f Apple, Inc. + |__ Port 005: Dev 003, If 0, Class=Human Interface Device, Driver=usbhid, 12M + ID 05ac:0291 Apple, Inc. + |__ Port 005: Dev 003, If 1, Class=Human Interface Device, Driver=usbhid, 12M + ID 05ac:0291 Apple, Inc. + |__ Port 005: Dev 003, If 2, Class=Human Interface Device, Driver=bcm5974, 12M + ID 05ac:0291 Apple, Inc. +/: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/4p, 5000M + ID 1d6b:0003 Linux Foundation 3.0 root hub +sudo dmidecode - - type system +# dmidecode 3.5 +Getting SMBIOS data from sysfs. +SMBIOS 2.4 present. +43 structures occupying 2616 bytes. +Table at 0x8CD10000. + +Wrong DMI structures length: 2616 bytes announced, only 2205 bytes available. +Handle 0x0000, DMI type 4, 35 bytes +Processor Information + Socket Designation: U3E1 + Type: Central Processor + Family: Core i5 + Manufacturer: Intel(R) Corporation + ID: 51 06 04 00 FF FB EB BF + Signature: Type 0, Family 6, Model 69, Stepping 1 + Flags: + FPU (Floating-point unit on-chip) + VME (Virtual mode extension) + DE (Debugging extension) + PSE (Page size extension) + TSC (Time stamp counter) + MSR (Model specific registers) + PAE (Physical address extension) + MCE (Machine check exception) + CX8 (CMPXCHG8 instruction supported) + APIC (On-chip APIC hardware supported) + SEP (Fast system call) + MTRR (Memory type range registers) + PGE (Page global enable) + MCA (Machine check architecture) + CMOV (Conditional move instruction supported) + PAT (Page attribute table) + PSE-36 (36-bit page size extension) + CLFSH (CLFLUSH instruction supported) + DS (Debug store) + ACPI (ACPI supported) + MMX (MMX technology supported) + FXSR (FXSAVE and FXSTOR instructions supported) + SSE (Streaming SIMD extensions) + SSE2 (Streaming SIMD extensions 2) + SS (Self-snoop) + HTT (Multi-threading) + TM (Thermal monitor supported) + PBE (Pending break enabled) + Version: Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz + Voltage: 0.9 V + External Clock: 25 MHz + Max Speed: 1400 MHz + Current Speed: 1400 MHz + Status: Populated, Enabled + Upgrade: Socket BGA1168 + L1 Cache Handle: 0x0002 + L2 Cache Handle: 0x0003 + L3 Cache Handle: 0x0004 + Serial Number: To Be Filled By O.E.M. + Asset Tag: To Be Filled By O.E.M. + Part Number: Not Specified + +Handle 0x0001, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 1 + Operational Mode: Write Back + Location: Internal + Installed Size: 64 kB + Maximum Size: 64 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Data + Associativity: 8-way Set-associative + +Handle 0x0002, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 1 + Operational Mode: Write Back + Location: Internal + Installed Size: 64 kB + Maximum Size: 64 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Instruction + Associativity: 8-way Set-associative + +Handle 0x0003, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 2 + Operational Mode: Write Back + Location: Internal + Installed Size: 512 kB + Maximum Size: 512 kB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Unified + Associativity: 8-way Set-associative + +Handle 0x0004, DMI type 7, 19 bytes +Cache Information + Socket Designation: Not Specified + Configuration: Enabled, Not Socketed, Level 3 + Operational Mode: Write Back + Location: Internal + Installed Size: 3 MB + Maximum Size: 3 MB + Supported SRAM Types: + Asynchronous + Installed SRAM Type: Asynchronous + Speed: Unknown + Error Correction Type: Single-bit ECC + System Type: Unified + Associativity: 12-way Set-associative + +Handle 0x0005, DMI type 16, 15 bytes +Physical Memory Array + Location: System Board Or Motherboard + Use: System Memory + Error Correction Type: None + Maximum Capacity: 8 GB + Error Information Handle: Not Provided + Number Of Devices: 2 + +Handle 0x0006, DMI type 130, 186 bytes +OEM-specific Type + Header and Data: + 82 BA 06 00 07 00 00 00 B0 00 92 10 F1 03 04 11 + 05 0B 03 11 01 08 0A 00 F4 01 78 78 90 50 90 11 + 50 E0 10 04 3C 3C 01 90 83 8C 00 A1 00 00 00 00 + 00 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 + FE 00 00 00 00 00 00 00 AF E6 45 42 4A 32 30 55 + 46 38 45 44 55 30 2D 47 4E 2D 46 20 30 20 02 FE + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 + +Handle 0x0007, DMI type 17, 34 bytes +Memory Device + Array Handle: 0x0005 + Error Information Handle: No Error + Total Width: Unknown + Data Width: Unknown + Size: 2 GB + Form Factor: SODIMM + Set: None + Locator: DIMM0 + Bank Locator: BANK 0 + Type: DDR3 + Type Detail: Synchronous + Speed: 1600 MT/s + Manufacturer: 0x02FE + Serial Number: 0x00000000 + Asset Tag: Unknown + Part Number: 0x45424A3230554638454455302D474E2D4620 + Rank: Unknown + Configured Memory Speed: Unknown + +Handle 0x0008, DMI type 130, 186 bytes +OEM-specific Type + Header and Data: + 82 BA 08 00 09 00 00 00 B0 00 92 10 F1 03 04 11 + 05 0B 03 11 01 08 0A 00 F4 01 78 78 90 50 90 11 + 50 E0 10 04 3C 3C 01 90 83 8C 00 A1 00 00 00 00 + 00 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 + FE 00 00 00 00 00 00 00 AF E6 45 42 4A 32 30 55 + 46 38 45 44 55 30 2D 47 4E 2D 46 20 30 20 02 FE + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 + +Handle 0x0009, DMI type 17, 34 bytes +Memory Device + Array Handle: 0x0005 + Error Information Handle: No Error + Total Width: Unknown + Data Width: Unknown + Size: 2 GB + Form Factor: SODIMM + Set: None + Locator: DIMM0 + Bank Locator: BANK 1 + Type: DDR3 + Type Detail: Synchronous + Speed: 1600 MT/s + Manufacturer: 0x02FE + Serial Number: 0x00000000 + Asset Tag: Unknown + Part Number: 0x45424A3230554638454455302D474E2D4620 + Rank: Unknown + Configured Memory Speed: Unknown + +Handle 0x000A, DMI type 19, 15 bytes +Memory Array Mapped Address + Starting Address: 0x00000000000 + Ending Address: 0x000FFFFFFFF + Range Size: 4 GB + Physical Array Handle: 0x0005 + Partition Width: 0 + +Handle 0x000B, DMI type 0, 24 bytes +BIOS Information + Vendor: Apple Inc. + Version: 431.140.6.0.0 + Release Date: 06/13/2021 + ROM Size: 8 MB + Characteristics: + PCI is supported + BIOS is upgradeable + BIOS shadowing is allowed + Boot from CD is supported + Selectable boot is supported + ACPI is supported + IEEE 1394 boot is supported + Smart battery is supported + Function key-initiated network boot is supported + BIOS Revision: 0.1 + +Handle 0x000C, DMI type 1, 27 bytes +System Information + Manufacturer: Apple Inc. + Product Name: MacBookAir6,2 + Version: 1.0 + Serial Number: C02NJSN2G085 + UUID: 46f5f24d-5e97-d651-876c-174121a74ad3 + Wake-up Type: Power Switch + SKU Number: System SKU# + Family: MacBook Air + +Handle 0x000D, DMI type 2, 16 bytes +Base Board Information + Manufacturer: Apple Inc. + Product Name: Mac-7DF21CB3ED6977E5 + Version: MacBookAir6,2 + Serial Number: C0244060HPWFY58AQ + Asset Tag: Base Board Asset Tag# + Features: + Board is a hosting board + Board is replaceable + Location In Chassis: Part Component + Chassis Handle: 0x000E + Type: Motherboard + Contained Object Handles: 0 + +Handle 0x000E, DMI type 3, 21 bytes +Chassis Information + Manufacturer: Apple Inc. + Type: Notebook + Lock: Not Present + Version: Mac-7DF21CB3ED6977E5 + Serial Number: C02NJSN2G085 + Asset Tag: Not Specified + Boot-up State: Safe + Power Supply State: Safe + Thermal State: Other + Security Status: Other + OEM Information: 0x00000000 + Height: Unspecified + Number Of Power Cords: Unspecified + Contained Elements: 0 + +Handle 0x000F, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: USB0 + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0010, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: USB1 + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0011, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Mini DisplayPort + External Connector Type: None + Port Type: Other + +Handle 0x0012, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: HDMI + External Connector Type: None + Port Type: Other + +Handle 0x0013, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Microphone + External Connector Type: None + Port Type: Other + +Handle 0x0014, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: Other + External Reference Designator: Speaker + External Connector Type: None + Port Type: Other + +Handle 0x0015, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: MagSafe DC Power + External Connector Type: Other + Port Type: Other + +Handle 0x0016, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: Audio Line Out + External Connector Type: Mini Jack (headphones) + Port Type: Audio Port + +Handle 0x0017, DMI type 8, 9 bytes +Port Connector Information + Internal Reference Designator: None + Internal Connector Type: None + External Reference Designator: SD Card Reader + External Connector Type: Access Bus (USB) + Port Type: USB + +Handle 0x0018, DMI type 9, 13 bytes +System Slot Information + Designation: AirPort + Type: x1 PCI Express + Current Usage: Available + Length: Short + ID: 0 + Characteristics: + 3.3 V is provided + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x0019, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 1 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 2 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001A, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 2 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 3 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001B, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 3 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 4 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001C, DMI type 9, 13 bytes +System Slot Information + Designation: Thunderbolt Slot 4 + Type: 32-bit PCI + Current Usage: Available + Length: Long + ID: 5 + Characteristics: + 3.3 V is provided + PME signal is supported + Hot-plug devices are supported + SMBus signal is supported + +Handle 0x001D, DMI type 10, 6 bytes +On Board Device Information + Type: Video + Status: Enabled + Description: Integrated Video Controller + +Handle 0x001E, DMI type 10, 6 bytes +On Board Device Information + Type: Sound + Status: Enabled + Description: Azalia Audio Codec + +Handle 0x001F, DMI type 10, 6 bytes +On Board Device Information + Type: Other + Status: Enabled + Description: SATA + +Handle 0x0020, DMI type 11, 5 bytes +OEM Strings + String 1: Apple ROM Version. Model: MBA61. EFI Version: 431.140 + String 2: .6.0.0. Built by: root@bb-g9-pdb92. Date: Sun Jun + String 3: 13 18:52:10 PDT 2021. Revision: 431.140.6 (B&I). ROM Ver + String 4: sion: F000_B00. Build Type: Official Build, Release. Compi + String 5: ler: clang-1205.0.19.59.6. + +Handle 0x0021, DMI type 12, 5 bytes +System Configuration Options + +Handle 0x0022, DMI type 13, 22 bytes +BIOS Language Information + Language Description Format: Long + Installable Languages: 1 + + Currently Installed Language: Not Specified + +Handle 0x0023, DMI type 32, 20 bytes +System Boot Information + Status: No errors detected + +Handle 0x0024, DMI type 131, 6 bytes +OEM-specific Type + Header and Data: + 83 06 24 00 05 06 + +Handle 0x0025, DMI type 128, 88 bytes +OEM-specific Type + Header and Data: + 80 58 25 00 03 00 00 00 77 F5 0F FB 7F FF 1F FF + 02 00 03 00 00 00 00 00 00 00 99 FF FF FF B2 FF + 00 00 B3 FF FF FF DD FF 00 00 E1 FF FF FF E3 FF + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + +Handle 0x0026, DMI type 133, 12 bytes +OEM-specific Type + Header and Data: + 85 0C 26 00 02 00 00 00 00 00 00 00 + +Handle 0x0027, DMI type 221, 26 bytes +OEM-specific Type + Header and Data: + DD 1A 27 00 03 01 00 01 08 00 00 00 02 00 00 00 + 00 26 00 03 00 01 03 00 01 00 + Strings: + Reference Code - CPU + uCode Version + TXT ACM version + +Wrong DMI structures count: 43 announced, only 40 decoded. +ip a +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host noprefixroute + valid_lft forever preferred_lft forever +2: wlp3s0: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 2c:f0:ee:2c:c9:b6 brd ff:ff:ff:ff:ff:ff + inet 192.168.0.129/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp3s0 + valid_lft 69028sec preferred_lft 69028sec + inet6 2403:580b:c4f1:0:2c29:38f5:cead:5105/64 scope global temporary dynamic + valid_lft 7176sec preferred_lft 3575sec + inet6 2403:580b:c4f1:0:f2da:ec3b:a12b:f463/64 scope global dynamic mngtmpaddr noprefixroute + valid_lft 7176sec preferred_lft 3575sec + inet6 fe80::a723:94df:1f0e:6489/64 scope link noprefixroute + valid_lft forever preferred_lft forever +iwconfig +lo no wireless extensions. + +wlp3s0 IEEE 802.11 ESSID:"interwebs" + Mode:Managed Frequency:2.422 GHz Access Point: 78:98:E8:66:04:49 + Retry short limit:7 RTS thr:off Fragment thr:off + Power Management:on + +nmcli device show +GENERAL.DEVICE: wlp3s0 +GENERAL.TYPE: wifi +GENERAL.HWADDR: 2C:F0:EE:2C:C9:B6 +GENERAL.MTU: 1500 +GENERAL.STATE: 100 (connected) +GENERAL.CONNECTION: interwebs +GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/7 +IP4.ADDRESS[1]: 192.168.0.129/24 +IP4.GATEWAY: 192.168.0.1 +IP4.ROUTE[1]: dst = 192.168.0.0/24, nh = 0.0.0.0, mt = 600 +IP4.ROUTE[2]: dst = 0.0.0.0/0, nh = 192.168.0.1, mt = 600 +IP4.DNS[1]: 192.168.0.1 +IP6.ADDRESS[1]: 2403:580b:c4f1:0:2c29:38f5:cead:5105/64 +IP6.ADDRESS[2]: 2403:580b:c4f1:0:f2da:ec3b:a12b:f463/64 +IP6.ADDRESS[3]: fe80::a723:94df:1f0e:6489/64 +IP6.GATEWAY: fe80::7a98:e8ff:fe66:445 +IP6.ROUTE[1]: dst = fe80::/64, nh = ::, mt = 1024 +IP6.ROUTE[2]: dst = 2403:580b:c4f1::/64, nh = ::, mt = 600 +IP6.ROUTE[3]: dst = 2403:580b:c4f1::/48, nh = fe80::7a98:e8ff:fe66:445, mt = 600 +IP6.ROUTE[4]: dst = ::/0, nh = fe80::7a98:e8ff:fe66:445, mt = 600 +IP6.DNS[1]: 2403:580b:c4f1::1 + +GENERAL.DEVICE: lo +GENERAL.TYPE: loopback +GENERAL.HWADDR: 00:00:00:00:00:00 +GENERAL.MTU: 65536 +GENERAL.STATE: 100 (connected (externally)) +GENERAL.CONNECTION: lo +GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/1 +IP4.ADDRESS[1]: 127.0.0.1/8 +IP4.GATEWAY: -- +IP6.ADDRESS[1]: ::1/128 +IP6.GATEWAY: -- diff --git a/ultrabunt-accessible.sh b/ultrabunt-accessible.sh index 26db352..0ea3107 100755 --- a/ultrabunt-accessible.sh +++ b/ultrabunt-accessible.sh @@ -22,24 +22,280 @@ TTS_PITCH="0" # Normal pitch TTS_VOLUME="10" # Slightly louder TTS_PUNCTUATION="some" # Read some punctuation for clarity -# Function to speak text using spd-say +# Function to check and install TTS dependencies +install_tts_dependencies() { + echo -e "${CYAN}Checking Text-to-Speech Dependencies...${NC}" + + # Check if running with sudo privileges + if [[ "$EUID" -ne 0 ]]; then + echo -e "${RED}Error: This script needs to be run with sudo to install dependencies.${NC}" + echo -e "${YELLOW}Please run: sudo ./ultrabunt-accessible.sh${NC}" + exit 1 + fi + + local packages_to_install=() + local missing_packages=() + + # Check for required packages + echo -e "${YELLOW}Checking required packages...${NC}" + + # Speech Dispatcher + if ! dpkg -l | grep -q "^ii.*speech-dispatcher "; then + missing_packages+=("speech-dispatcher") + echo -e " ${RED}✗${NC} speech-dispatcher (not installed)" + else + echo -e " ${GREEN}✓${NC} speech-dispatcher (installed)" + fi + + # PulseAudio + if ! dpkg -l | grep -q "^ii.*pulseaudio "; then + missing_packages+=("pulseaudio") + echo -e " ${RED}✗${NC} pulseaudio (not installed)" + else + echo -e " ${GREEN}✓${NC} pulseaudio (installed)" + fi + + # ALSA utilities + if ! dpkg -l | grep -q "^ii.*alsa-utils "; then + missing_packages+=("alsa-utils") + echo -e " ${RED}✗${NC} alsa-utils (not installed)" + else + echo -e " ${GREEN}✓${NC} alsa-utils (installed)" + fi + + # PulseAudio utilities + if ! dpkg -l | grep -q "^ii.*pulseaudio-utils "; then + missing_packages+=("pulseaudio-utils") + echo -e " ${RED}✗${NC} pulseaudio-utils (not installed)" + else + echo -e " ${GREEN}✓${NC} pulseaudio-utils (installed)" + fi + + # Festival speech synthesis (fallback TTS engine) + if ! dpkg -l | grep -q "^ii.*festival "; then + missing_packages+=("festival") + echo -e " ${RED}✗${NC} festival (not installed)" + else + echo -e " ${GREEN}✓${NC} festival (installed)" + fi + + # eSpeak (another TTS engine) + if ! dpkg -l | grep -q "^ii.*espeak "; then + missing_packages+=("espeak") + echo -e " ${RED}✗${NC} espeak (not installed)" + else + echo -e " ${GREEN}✓${NC} espeak (installed)" + fi + + # If packages are missing, offer to install them + if [[ ${#missing_packages[@]} -gt 0 ]]; then + echo "" + echo -e "${YELLOW}Missing packages detected:${NC}" + for package in "${missing_packages[@]}"; do + echo -e " • $package" + done + echo "" + echo -e "${CYAN}These packages are required for text-to-speech functionality.${NC}" + echo -e "${WHITE}Would you like to install them now? (y/n)${NC}" + read -p "Install missing packages? " -n 1 -r + echo + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo -e "${GREEN}Installing missing packages...${NC}" + + # Update package list + echo -e "${YELLOW}Updating package list...${NC}" + apt update + + # Install missing packages + for package in "${missing_packages[@]}"; do + echo -e "${YELLOW}Installing $package...${NC}" + if apt install -y "$package"; then + echo -e "${GREEN}✓ $package installed successfully${NC}" + else + echo -e "${RED}✗ Failed to install $package${NC}" + fi + done + + # Configure audio system + echo -e "${YELLOW}Configuring audio system...${NC}" + + # Start PulseAudio if not running + if ! pgrep -x "pulseaudio" > /dev/null && ! pgrep -x "pipewire" > /dev/null; then + echo -e "${YELLOW}Starting PulseAudio...${NC}" + # Get the actual user (not root) if running with sudo + local actual_user="${SUDO_USER:-$USER}" + if [[ -n "$actual_user" && "$actual_user" != "root" ]]; then + sudo -u "$actual_user" pulseaudio --start 2>/dev/null || true + fi + else + echo -e "${GREEN}✓ Audio system is running (PulseAudio/PipeWire)${NC}" + fi + + # Configure Speech Dispatcher + echo -e "${YELLOW}Configuring Speech Dispatcher...${NC}" + + # Stop Speech Dispatcher service first + systemctl stop speech-dispatcher 2>/dev/null || true + + # Create basic Speech Dispatcher config if it doesn't exist + local spd_config_dir="/etc/speech-dispatcher" + if [[ -d "$spd_config_dir" ]]; then + # Ensure Speech Dispatcher can use available TTS engines + if [[ -f "$spd_config_dir/speechd.conf" ]]; then + # Enable espeak as default if available + if command -v espeak >/dev/null 2>&1; then + sed -i 's/^#.*DefaultModule.*espeak/DefaultModule espeak/' "$spd_config_dir/speechd.conf" 2>/dev/null || true + echo -e " ${GREEN}✓${NC} Set espeak as default TTS engine" + fi + fi + + # Create user-specific Speech Dispatcher config + local actual_user="${SUDO_USER:-$USER}" + if [[ -n "$actual_user" && "$actual_user" != "root" ]]; then + local user_spd_dir="/home/$actual_user/.config/speech-dispatcher" + sudo -u "$actual_user" mkdir -p "$user_spd_dir" 2>/dev/null || true + + # Create a simple user config that prefers espeak with female voice + if [[ ! -f "$user_spd_dir/speechd.conf" ]]; then + sudo -u "$actual_user" cat > "$user_spd_dir/speechd.conf" << 'EOF' +# User Speech Dispatcher Configuration +LogLevel 3 +DefaultModule espeak +DefaultLanguage en +DefaultRate -20 +DefaultPitch 0 +DefaultVolume 100 +DefaultVoiceType female1 +EOF + echo -e " ${GREEN}✓${NC} Created user Speech Dispatcher config with female voice" + fi + + # Also create espeak-specific config for female voice + if [[ ! -f "$user_spd_dir/modules/espeak.conf" ]]; then + sudo -u "$actual_user" mkdir -p "$user_spd_dir/modules" 2>/dev/null || true + sudo -u "$actual_user" cat > "$user_spd_dir/modules/espeak.conf" << 'EOF' +# Espeak module configuration +EspeakDefaultVoice "en+f3" +EspeakRate -20 +EspeakPitch 0 +EspeakVolume 100 +EOF + echo -e " ${GREEN}✓${NC} Created espeak module config with female voice" + fi + fi + fi + + # Restart Speech Dispatcher service + echo -e "${YELLOW}Restarting Speech Dispatcher service...${NC}" + systemctl start speech-dispatcher 2>/dev/null || true + sleep 2 # Give it time to start + + # Check if Speech Dispatcher is running + if systemctl is-active --quiet speech-dispatcher 2>/dev/null; then + echo -e " ${GREEN}✓${NC} Speech Dispatcher service is running" + else + echo -e " ${YELLOW}⚠${NC} Speech Dispatcher service may not be running properly" + fi + + # Test audio output + echo -e "${YELLOW}Testing audio system...${NC}" + if command -v speaker-test >/dev/null 2>&1; then + echo -e "${CYAN}Running brief audio test (2 seconds)...${NC}" + timeout 2s speaker-test -t sine -f 1000 -l 1 2>/dev/null || echo -e "${YELLOW}Audio test completed (or no audio device available)${NC}" + fi + + echo -e "${GREEN}✓ Dependencies installation completed!${NC}" + echo "" + else + echo -e "${YELLOW}Skipping package installation. TTS may not work properly.${NC}" + echo -e "${CYAN}You can install them manually later with:${NC}" + echo -e "${WHITE}sudo apt update && sudo apt install ${missing_packages[*]}${NC}" + echo "" + fi + else + echo -e "${GREEN}✓ All required packages are already installed!${NC}" + fi + + # Additional audio system checks + echo -e "${YELLOW}Performing audio system checks...${NC}" + + # Check if audio devices are available + if command -v aplay >/dev/null 2>&1; then + local audio_devices=$(aplay -l 2>/dev/null | grep -c "card" || echo "0") + if [[ "$audio_devices" -gt 0 ]]; then + echo -e " ${GREEN}✓${NC} Audio devices found ($audio_devices)" + else + echo -e " ${YELLOW}⚠${NC} No audio devices detected" + fi + fi + + # Check PulseAudio/PipeWire status + if command -v pulseaudio >/dev/null 2>&1 || command -v pipewire >/dev/null 2>&1; then + if pgrep -x "pulseaudio" > /dev/null || pgrep -x "pipewire" > /dev/null; then + echo -e " ${GREEN}✓${NC} Audio system is running (PulseAudio/PipeWire)" + else + echo -e " ${YELLOW}⚠${NC} Audio system is not running" + fi + fi + + # Check if pactl (PulseAudio control) works + if command -v pactl >/dev/null 2>&1; then + if pactl info >/dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} PulseAudio control interface is working" + else + echo -e " ${YELLOW}⚠${NC} PulseAudio control interface not responding" + fi + fi + + echo "" +} + +# Function to speak text using spd-say or fallback TTS engines speak_text() { local text="$1" local priority="${2:-text}" # Default priority is 'text' - if [[ "$TTS_ENABLED" == true ]] && command -v spd-say >/dev/null 2>&1; then + if [[ "$TTS_ENABLED" == true ]]; then # Clean the text of ANSI color codes for speech local clean_text=$(echo "$text" | sed 's/\x1b\[[0-9;]*m//g') - spd-say \ - --voice-type "$TTS_VOICE" \ - --rate "$TTS_RATE" \ - --pitch "$TTS_PITCH" \ - --volume "$TTS_VOLUME" \ - --punctuation-mode "$TTS_PUNCTUATION" \ - --priority "$priority" \ - --wait \ - "$clean_text" 2>/dev/null + # Kill any existing TTS processes to prevent overlap + pkill -f "spd-say" 2>/dev/null || true + pkill -f "espeak" 2>/dev/null || true + + # Try spd-say first if no fallback is set + if [[ -z "$TTS_FALLBACK" ]] && command -v spd-say >/dev/null 2>&1; then + # Use spd-say with wait flag to prevent overlapping + timeout 10s spd-say \ + --voice-type "$TTS_VOICE" \ + --rate "$TTS_RATE" \ + --pitch "$TTS_PITCH" \ + --volume "$TTS_VOLUME" \ + --punctuation-mode "$TTS_PUNCTUATION" \ + --priority "$priority" \ + --wait \ + "$clean_text" 2>/dev/null + + # Use fallback TTS engines + elif [[ "$TTS_FALLBACK" == "espeak" ]] && command -v espeak >/dev/null 2>&1; then + # Use espeak directly with similar settings + local espeak_rate=$((TTS_RATE + 175)) # Convert spd-say rate to espeak rate (approx) + # Ensure espeak_rate is within valid range (80-450) + if [[ $espeak_rate -lt 80 ]]; then espeak_rate=80; fi + if [[ $espeak_rate -gt 450 ]]; then espeak_rate=450; fi + + timeout 10s espeak \ + -s "$espeak_rate" \ + -p "$((TTS_PITCH + 50))" \ + -a "$((TTS_VOLUME * 20))" \ + -v "en+f3" \ + "$clean_text" 2>/dev/null + + elif [[ "$TTS_FALLBACK" == "festival" ]] && command -v festival >/dev/null 2>&1; then + # Use festival for TTS + timeout 10s echo "$clean_text" | festival --tts 2>/dev/null + fi fi } @@ -62,10 +318,77 @@ check_tts_availability() { echo -e "${WHITE}sudo apt update && sudo apt install speech-dispatcher${NC}" echo "" TTS_ENABLED=false - read -p "Press Enter to continue without TTS, or Ctrl+C to exit and install Speech Dispatcher..." + echo -e "${WHITE}Press Enter to continue without TTS, or Ctrl+C to exit and install Speech Dispatcher...${NC}" + read + return 1 + fi + + echo -e "${YELLOW}Testing Speech Dispatcher functionality...${NC}" + + # Test if Speech Dispatcher is actually working with multiple methods + local spd_working=false + + # Method 1: Test with spd-say directly + echo -e "${CYAN}Testing spd-say directly...${NC}" + if timeout 5s spd-say -w "Testing speech" 2>/dev/null; then + echo -e "${GREEN}✓ spd-say direct test successful${NC}" + spd_working=true + else + echo -e "${RED}✗ spd-say direct test failed${NC}" + fi + + # Method 2: Test with different TTS engines if spd-say failed + if [[ "$spd_working" == false ]]; then + echo -e "${CYAN}Testing alternative TTS engines...${NC}" + + # Try espeak directly + if command -v espeak >/dev/null 2>&1; then + echo -e "${YELLOW}Trying espeak directly...${NC}" + if timeout 3s espeak "Testing espeak" 2>/dev/null; then + echo -e "${GREEN}✓ espeak works directly${NC}" + # Update speak_text function to use espeak as fallback + TTS_FALLBACK="espeak" + spd_working=true + else + echo -e "${RED}✗ espeak test failed${NC}" + fi + fi + + # Try festival if espeak failed + if [[ "$spd_working" == false ]] && command -v festival >/dev/null 2>&1; then + echo -e "${YELLOW}Trying festival...${NC}" + if timeout 3s echo "Testing festival" | festival --tts 2>/dev/null; then + echo -e "${GREEN}✓ festival works${NC}" + TTS_FALLBACK="festival" + spd_working=true + else + echo -e "${RED}✗ festival test failed${NC}" + fi + fi + fi + + # Final result + if [[ "$spd_working" == true ]]; then + echo -e "${GREEN}✓ Text-to-speech is working!${NC}" + TTS_ENABLED=true + if [[ -n "$TTS_FALLBACK" ]]; then + echo -e "${YELLOW}Note: Using $TTS_FALLBACK as fallback TTS engine${NC}" + fi + return 0 + else + echo -e "${RED}Warning: Speech Dispatcher found but not working properly!${NC}" + echo -e "${YELLOW}This might be due to audio system issues or configuration problems.${NC}" + echo -e "${CYAN}Troubleshooting steps:${NC}" + echo -e "${WHITE}1. Check if PulseAudio is running: pulseaudio --check${NC}" + echo -e "${WHITE}2. Test audio: speaker-test -c2 -t wav${NC}" + echo -e "${WHITE}3. Restart Speech Dispatcher: sudo systemctl restart speech-dispatcher${NC}" + echo -e "${WHITE}4. Check Speech Dispatcher status: systemctl status speech-dispatcher${NC}" + echo "" + TTS_ENABLED=false + echo -e "${WHITE}Press Enter to continue without TTS...${NC}" + read return 1 fi - return 0 } # Function to test TTS and let user adjust settings @@ -78,6 +401,9 @@ test_and_configure_tts() { announce "Testing your text-to-speech settings..." "$YELLOW" speak_text "This is a test of the text to speech system. Can you hear this clearly?" + # Give time for speech to complete + sleep 2 + echo "" echo -e "${WHITE}TTS Settings Test Complete${NC}" echo -e "${CYAN}Current settings:${NC}" @@ -109,26 +435,39 @@ test_and_configure_tts() { 2) TTS_RATE="-40" announce "Voice speed set to slower. Testing..." "$CYAN" + # Update Speech Dispatcher config if needed + update_spd_config speak_text "This is the new slower speed setting." + sleep 3 ;; 3) TTS_RATE="0" announce "Voice speed set to normal. Testing..." "$CYAN" + # Update Speech Dispatcher config if needed + update_spd_config speak_text "This is the normal speed setting." + sleep 3 ;; 4) TTS_VOLUME="30" announce "Voice volume set to louder. Testing..." "$CYAN" + # Update Speech Dispatcher config if needed + update_spd_config speak_text "This is the louder volume setting." + sleep 3 ;; 5) TTS_VOLUME="0" announce "Voice volume set to normal. Testing..." "$CYAN" + # Update Speech Dispatcher config if needed + update_spd_config speak_text "This is the normal volume setting." + sleep 3 ;; 6) announce "Testing current settings..." "$YELLOW" speak_text "This is a test of your current text to speech settings. How does this sound?" + sleep 2 ;; 7) TTS_ENABLED=false @@ -143,6 +482,33 @@ test_and_configure_tts() { fi } +# Function to update Speech Dispatcher configuration with current settings +update_spd_config() { + # Only update if we have a user config directory + local actual_user="${SUDO_USER:-$USER}" + if [[ -n "$actual_user" && "$actual_user" != "root" ]]; then + local user_spd_dir="/home/$actual_user/.config/speech-dispatcher" + + # Update the main config file if it exists + if [[ -f "$user_spd_dir/speechd.conf" ]]; then + # Update rate setting + sed -i "s/^DefaultRate.*/DefaultRate $TTS_RATE/" "$user_spd_dir/speechd.conf" 2>/dev/null || true + # Update volume setting + sed -i "s/^DefaultVolume.*/DefaultVolume $TTS_VOLUME/" "$user_spd_dir/speechd.conf" 2>/dev/null || true + fi + + # Update espeak module config if it exists + if [[ -f "$user_spd_dir/modules/espeak.conf" ]]; then + sed -i "s/^EspeakRate.*/EspeakRate $TTS_RATE/" "$user_spd_dir/modules/espeak.conf" 2>/dev/null || true + sed -i "s/^EspeakVolume.*/EspeakVolume $TTS_VOLUME/" "$user_spd_dir/modules/espeak.conf" 2>/dev/null || true + fi + + # Restart Speech Dispatcher to apply changes + systemctl restart speech-dispatcher 2>/dev/null || true + sleep 1 + fi +} + # Function to provide audio navigation help provide_navigation_help() { announce "Navigation Help for Ultrabunt Installer" "$CYAN" "important" @@ -184,13 +550,21 @@ main() { exit 1 fi + # Install TTS dependencies if needed (requires sudo) + if [[ "$EUID" -eq 0 ]]; then + install_tts_dependencies + else + echo -e "${YELLOW}Note: Running without sudo. If TTS doesn't work, try: sudo ./ultrabunt-accessible.sh${NC}" + echo "" + fi + # Check TTS availability check_tts_availability # Welcome and TTS configuration echo -e "${PURPLE}╔══════════════════════════════════════════════════════════════════════════════╗${NC}" echo -e "${PURPLE}║ ULTRABUNT ACCESSIBLE INSTALLER ║${NC}" - echo -e "${PURPLE}║ Enhanced for Visually Impaired Users ║${NC}" + echo -e "${PURPLE}║ Enhanced for Visually Impaired Users ║${NC}" echo -e "${PURPLE}╚══════════════════════════════════════════════════════════════════════════════╝${NC}" echo "" diff --git a/ultrabunt.sh b/ultrabunt.sh index b2178ed..6215de3 100755 --- a/ultrabunt.sh +++ b/ultrabunt.sh @@ -218,21 +218,29 @@ show_ascii_splash() { " .:--::+*:..::::::............+#*-::::.:-. " " :----:...+*:...:::.............:+*:..:....: " " -:.... -##-:::::.:......:+#= ...... " - " . -*%%#####%%#=. . .:::. " - " =@@ .#@-+@%@@+*@* @#-@@%*:@@@%.@@%+*@@%@# " - " =@@#.#@@=#@ +@++@@@@%=@# %# @% -@* %# " - " +@#@@%%@.*@ *@*-@%@@% @+ @# @@%-=@@%@- " - " =@+=%=#@.*@#%@+-@ =@#.#=@@: @# %@*:-@#-@+ " - " .====: . .=**= . :=++ .=**= :=++++- " - " -#%%%@@: %@%++@@#.=#%%%@@..%@%++%@#+#@@%#+ " - " +@*@@= -@*:---=%@:#@- =@* #@= " - " +@@@@@# @%: :@*#@%##%@+%%: -@# #@= " - " *@=.*@+ -@%=::=@%-*@=::+@%-@%=::=@%: #@=. " - " =%- +%- .=#@@#=. +%@@@%+ .=#@@#=. +%= " - " @%##* +@#*@#. @###=#@@#+ " - " @@% --%: %# @: *% " - " =@-%+. -@+ #@@% *% " - " ####= .+##*. # +* " + " ██████ ██████ █████ █████ ████ ██████ █████████ ███████ ████████ " + "░░██████ ██████ ███░░░███░░████ ░░██ ███░░░███░█░░░██░░█░░██░░░█░░███░░███ " + " ░███░█████░███ ███ ░░███░██░██ ░██ ░███ ░░░ ░ ░██ ░ ░██ █ ░███ ░███ " + " ░███░░███ ░███░███ ░███░██ ░██░██ ░░███████ ░██ ░████ ░██████ " + " ░███ ░░░ ░███░███ ░███░██ ░████ ░░░░███ ░██ ░██░█ ░███░░███ " + " ░███ ░███░░███ ███ ░███ ░███ ███ ░███ ░██ ░██ █ ░███ ░███ " + " █████ █████░░░█████░ ████ ░████░░███████ █████ ███████ ████ ████" + "░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░ ░░░░░░░ ░░░░ ░░░░░░ ░░░░ ░░░░ " + " ███████████ ███████ ███████████ ███████ ███████████ " + "░░███░░░░░███ ███░░░░░███░░███░░░░░███ ███░░░░░███ ░█░░░███░░░█ " + " ░███ ░███ ███ ░░███░███ ░███ ███ ░░███░ ░███ ░ " + " ░██████████ ░███ ░███░██████████ ░███ ░███ ░███ " + " ░███░░░░░███░███ ░███░███░░░░░███░███ ░███ ░███ " + " ░███ ░███░░███ ███ ░███ ░███░░███ ███ ░███ " + " █████ █████░░░███████░ ███████████ ░░░███████░ █████ " + "░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░░░ ░░░░░░░ ░░░░░ " + " █████████ ███████ ███████████ ███████████ " + " ███░░░░░███ ███░░░░░███░░███░░░░░░█░█░░░███░░░█ " + "░███ ░░░ ███ ░░███░███ █ ░ ░ ░███ ░ " + "░░█████████ ░███ ░███░███████ ░███ " + " ░░░░░░░░███░███ ░███░███░░░█ ░███ " + " ███ ░███░░███ ███ ░███ ░ ░███ " + "░░█████████ ░░░███████░ █████ █████ " ) # Display each line with scrolling effect @@ -600,6 +608,11 @@ log_error() { echo "$msg" | tee -a "$LOGFILE" >&2 } +log_warning() { + local msg="[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $*" + echo "$msg" | tee -a "$LOGFILE" >&2 +} + # Ensure whiptail is available ensure_deps() { log "Checking dependencies..." @@ -628,7 +641,7 @@ ui_msg() { } ui_info() { - whiptail --title "$1" --msgbox "$2" 20 80 + whiptail --title "$1" --msgbox "$2" 30 100 --scrolltext } ui_yesno() { @@ -707,30 +720,67 @@ get_available_methods() { methods+=("$base_name" "$desc") fi - # Check for alternative methods in preferred order: APT → Snap → Flatpak → Custom - for variant in "${base_name}-deb" "${base_name}-snap" "${base_name}-flatpak"; do + # Check for alternative methods in preferred order: APT → Snap → Flatpak → Deb + # Prefer repository-backed installs (APT/Snap) before Flatpak and standalone Deb + for variant in "${base_name}-snap" "${base_name}-flatpak" "${base_name}-deb"; do if [[ -n "${PACKAGES[$variant]:-}" ]]; then local desc="${PKG_DESC[$variant]}" + # Annotate snap variants if they require classic confinement + if [[ "$variant" == "${base_name}-snap" ]]; then + local snap_pkg="${PACKAGES[$variant]}" + if [[ -n "$snap_pkg" ]] && requires_classic_snap "$snap_pkg"; then + desc+=" [classic confinement]" + fi + fi methods+=("$variant" "$desc") fi done - # Special cases for apps with different naming patterns + # Special cases for apps with different naming patterns (avoid duplicates) case "$base_name" in "vscode") if [[ -n "${PACKAGES[vscode-snap]:-}" ]]; then - methods+=("vscode-snap" "${PKG_DESC[vscode-snap]}") + local found=0 + for ((i=0; i<${#methods[@]}; i+=2)); do + [[ "${methods[i]}" == "vscode-snap" ]] && found=1 && break + done + if [[ $found -eq 0 ]]; then + local desc="${PKG_DESC[vscode-snap]}" + local snap_pkg="${PACKAGES[vscode-snap]}" + if [[ -n "$snap_pkg" ]] && requires_classic_snap "$snap_pkg"; then + desc+=" [classic confinement]" + fi + methods+=("vscode-snap" "$desc") + fi fi if [[ -n "${PACKAGES[vscode-flatpak]:-}" ]]; then - methods+=("vscode-flatpak" "${PKG_DESC[vscode-flatpak]}") + local found=0 + for ((i=0; i<${#methods[@]}; i+=2)); do + [[ "${methods[i]}" == "vscode-flatpak" ]] && found=1 && break + done + if [[ $found -eq 0 ]]; then + methods+=("vscode-flatpak" "${PKG_DESC[vscode-flatpak]}") + fi fi ;; "discord") - if [[ -n "${PACKAGES[discord-deb]:-}" ]]; then - methods+=("discord-deb" "${PKG_DESC[discord-deb]}") - fi if [[ -n "${PACKAGES[discord-flatpak]:-}" ]]; then - methods+=("discord-flatpak" "${PKG_DESC[discord-flatpak]}") + local found=0 + for ((i=0; i<${#methods[@]}; i+=2)); do + [[ "${methods[i]}" == "discord-flatpak" ]] && found=1 && break + done + if [[ $found -eq 0 ]]; then + methods+=("discord-flatpak" "${PKG_DESC[discord-flatpak]}") + fi + fi + if [[ -n "${PACKAGES[discord-deb]:-}" ]]; then + local found=0 + for ((i=0; i<${#methods[@]}; i+=2)); do + [[ "${methods[i]}" == "discord-deb" ]] && found=1 && break + done + if [[ $found -eq 0 ]]; then + methods+=("discord-deb" "${PKG_DESC[discord-deb]}") + fi fi ;; esac @@ -804,12 +854,18 @@ is_apt_installed() { return 1 fi - # Use cache for fast lookup + # Use cache for fast lookup first if [[ -n "${INSTALLED_CACHE["apt:$pkg"]:-}" ]]; then return 0 - else - return 1 fi + + # Fallback to direct dpkg check when cache is missing + if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then + INSTALLED_CACHE["apt:$pkg"]=1 + return 0 + fi + + return 1 } is_snap_installed() { @@ -1002,9 +1058,9 @@ PKG_DESC[rustup]="Rust toolchain installer [CURL]" PKG_METHOD[rustup]="custom" PKG_CATEGORY[rustup]="dev" -PACKAGES[poetry]="poetry" -PKG_DESC[poetry]="Modern Python dependency manager [PIP]" -PKG_METHOD[poetry]="custom" +PACKAGES[poetry]="python3-poetry" +PKG_DESC[poetry]="Modern Python dependency manager [APT]" +PKG_METHOD[poetry]="apt" PKG_CATEGORY[poetry]="dev" PACKAGES[pipx]="pipx" @@ -1023,8 +1079,8 @@ PKG_METHOD[bun]="custom" PKG_CATEGORY[bun]="dev" PACKAGES[mkdocs]="mkdocs" -PKG_DESC[mkdocs]="Static site generator for docs (dev favorite) [PIP]" -PKG_METHOD[mkdocs]="custom" +PKG_DESC[mkdocs]="Static site generator for docs (dev favorite) [APT]" +PKG_METHOD[mkdocs]="apt" PKG_CATEGORY[mkdocs]="dev" PACKAGES[insomnia]="insomnia" @@ -1686,6 +1742,12 @@ PKG_METHOD[localsend]="snap" PKG_CATEGORY[localsend]="communication" PKG_DEPS[localsend]="snapd" +# SNAP MANAGER (dependency for snap apps) +PACKAGES[snapd]="snapd" +PKG_DESC[snapd]="Snap daemon and CLI [APT]" +PKG_METHOD[snapd]="apt" +PKG_CATEGORY[snapd]="system" + # SNAP APPS PACKAGES[spotify]="spotify" PKG_DESC[spotify]="Music streaming service [SNP]" @@ -1767,7 +1829,7 @@ PKG_CATEGORY[seafile]="cloud" # TERMINALS PACKAGES[warp-terminal]="warp-terminal" -PKG_DESC[warp-terminal]="Modern terminal with AI features [DEB]" +PKG_DESC[warp-terminal]="Modern terminal with AI features [APT]" PKG_METHOD[warp-terminal]="custom" PKG_CATEGORY[warp-terminal]="terminals" @@ -1907,10 +1969,7 @@ PKG_DESC[invokeai]="Stable Diffusion image generator [PYTHON]" PKG_METHOD[invokeai]="custom" PKG_CATEGORY[invokeai]="ai" -PACKAGES[lmdeploy]="lmdeploy" -PKG_DESC[lmdeploy]="Lightweight framework to serve LLMs efficiently [PIP]" -PKG_METHOD[lmdeploy]="pip" -PKG_CATEGORY[lmdeploy]="ai" + PACKAGES[koboldcpp]="koboldcpp" PKG_DESC[koboldcpp]="LLM interface optimized for story and RP generation [BIN]" @@ -1937,35 +1996,7 @@ PKG_DESC[kohya-ss-gui]="Fine-tune and train models with GUI [GIT]" PKG_METHOD[kohya-ss-gui]="custom" PKG_CATEGORY[kohya-ss-gui]="ai" -PACKAGES[faster-whisper]="faster-whisper" -PKG_DESC[faster-whisper]="Optimized Python Whisper with CTranslate2 [PIP]" -PKG_METHOD[faster-whisper]="pip" -PKG_CATEGORY[faster-whisper]="ai" -PACKAGES[whisperx]="whisperx" -PKG_DESC[whisperx]="Whisper with speaker diarization support [PIP]" -PKG_METHOD[whisperx]="pip" -PKG_CATEGORY[whisperx]="ai" - -PACKAGES[coqui-stt]="coqui-stt" -PKG_DESC[coqui-stt]="Open-source speech-to-text engine [PIP]" -PKG_METHOD[coqui-stt]="pip" -PKG_CATEGORY[coqui-stt]="ai" - -PACKAGES[piper-tts]="piper-tts" -PKG_DESC[piper-tts]="Modern local text-to-speech by Rhasspy [PIP]" -PKG_METHOD[piper-tts]="pip" -PKG_CATEGORY[piper-tts]="ai" - -PACKAGES[mimic3]="mimic3" -PKG_DESC[mimic3]="Neural voice synthesis by Mycroft AI [PIP]" -PKG_METHOD[mimic3]="pip" -PKG_CATEGORY[mimic3]="ai" - -PACKAGES[coqui-tts]="coqui-tts" -PKG_DESC[coqui-tts]="Deep-learning TTS with voice cloning [PIP]" -PKG_METHOD[coqui-tts]="pip" -PKG_CATEGORY[coqui-tts]="ai" PACKAGES[ffmpeg]="ffmpeg" PKG_DESC[ffmpeg]="Complete multimedia processing toolkit [APT]" @@ -1973,8 +2004,8 @@ PKG_METHOD[ffmpeg]="apt" PKG_CATEGORY[ffmpeg]="video" PACKAGES[yt-dlp]="yt-dlp" -PKG_DESC[yt-dlp]="Modern YouTube/media downloader (youtube-dl fork) [PIP]" -PKG_METHOD[yt-dlp]="custom" +PKG_DESC[yt-dlp]="Modern YouTube/media downloader (youtube-dl fork) [APT]" +PKG_METHOD[yt-dlp]="apt" PKG_CATEGORY[yt-dlp]="video" PACKAGES[freetube]="freetube" @@ -2215,6 +2246,150 @@ PKG_DESC[n8n]="Workflow automation tool (self-hosted Zapier alternative) [NPM]" PKG_METHOD[n8n]="custom" PKG_CATEGORY[n8n]="dev" +# ============================================================================== +# SINGLE BOARD COMPUTERS & MICROCONTROLLERS +# ============================================================================== + +# Raspberry Pi Tools +PACKAGES[rpi-imager]="rpi-imager" +PKG_DESC[rpi-imager]="Official Raspberry Pi Imager for flashing OS images to SD cards [APT]" +PKG_METHOD[rpi-imager]="apt" +PKG_CATEGORY[rpi-imager]="sbc" + +PACKAGES[balena-etcher]="balena-etcher-electron" +PKG_DESC[balena-etcher]="Flash OS images to SD cards and USB drives [DEB]" +PKG_METHOD[balena-etcher]="custom" +PKG_CATEGORY[balena-etcher]="sbc" + +PACKAGES[rpi-config]="raspi-config" +PKG_DESC[rpi-config]="Raspberry Pi configuration tool [APT]" +PKG_METHOD[rpi-config]="apt" +PKG_CATEGORY[rpi-config]="sbc" + +# Arduino Development +PACKAGES[arduino-ide]="arduino-ide" +PKG_DESC[arduino-ide]="Arduino IDE 2.0 - Modern Arduino development environment [DEB]" +PKG_METHOD[arduino-ide]="custom" +PKG_CATEGORY[arduino-ide]="sbc" + +PACKAGES[arduino-cli]="arduino-cli" +PKG_DESC[arduino-cli]="Arduino command line interface [BINARY]" +PKG_METHOD[arduino-cli]="custom" +PKG_CATEGORY[arduino-cli]="sbc" + +PACKAGES[platformio]="platformio" +PKG_DESC[platformio]="Professional collaborative platform for embedded development [SNP]" +PKG_METHOD[platformio]="snap" +PKG_CATEGORY[platformio]="sbc" + +# ESP32/ESP8266 Tools +PACKAGES[esptool]="python3-esptool" +PKG_DESC[esptool]="ESP32/ESP8266 ROM bootloader utility [APT]" +PKG_METHOD[esptool]="apt" +PKG_CATEGORY[esptool]="sbc" + +PACKAGES[esp-idf]="esp-idf" +PKG_DESC[esp-idf]="Espressif IoT Development Framework [CUSTOM]" +PKG_METHOD[esp-idf]="custom" +PKG_CATEGORY[esp-idf]="sbc" + +# Cross-compilation and embedded tools +PACKAGES[gcc-arm-none-eabi]="gcc-arm-none-eabi" +PKG_DESC[gcc-arm-none-eabi]="ARM Cortex-M and Cortex-R cross-compiler [APT]" +PKG_METHOD[gcc-arm-none-eabi]="apt" +PKG_CATEGORY[gcc-arm-none-eabi]="sbc" + +PACKAGES[openocd]="openocd" +PKG_DESC[openocd]="Open On-Chip Debugger for ARM and other processors [APT]" +PKG_METHOD[openocd]="apt" +PKG_CATEGORY[openocd]="sbc" + +PACKAGES[gdb-multiarch]="gdb-multiarch" +PKG_DESC[gdb-multiarch]="GNU Debugger with support for multiple architectures [APT]" +PKG_METHOD[gdb-multiarch]="apt" +PKG_CATEGORY[gdb-multiarch]="sbc" + +# Orange Pi and other SBC tools +PACKAGES[sunxi-tools]="sunxi-tools" +PKG_DESC[sunxi-tools]="Tools for Allwinner SoCs (Orange Pi, Banana Pi) [APT]" +PKG_METHOD[sunxi-tools]="apt" +PKG_CATEGORY[sunxi-tools]="sbc" + +PACKAGES[u-boot-tools]="u-boot-tools" +PKG_DESC[u-boot-tools]="Das U-Boot bootloader utilities [APT]" +PKG_METHOD[u-boot-tools]="apt" +PKG_CATEGORY[u-boot-tools]="sbc" + +# Serial communication +PACKAGES[minicom]="minicom" +PKG_DESC[minicom]="Serial communication program [APT]" +PKG_METHOD[minicom]="apt" +PKG_CATEGORY[minicom]="sbc" + +PACKAGES[screen]="screen" +PKG_DESC[screen]="Terminal multiplexer with serial support [APT]" +PKG_METHOD[screen]="apt" +PKG_CATEGORY[screen]="sbc" + +PACKAGES[picocom]="picocom" +PKG_DESC[picocom]="Minimal dumb-terminal emulation program [APT]" +PKG_METHOD[picocom]="apt" +PKG_CATEGORY[picocom]="sbc" + +# GPIO and hardware interfacing +PACKAGES[wiringpi]="wiringpi" +PKG_DESC[wiringpi]="GPIO interface library for Raspberry Pi [APT]" +PKG_METHOD[wiringpi]="apt" +PKG_CATEGORY[wiringpi]="sbc" + +PACKAGES[rpi-gpio]="python3-rpi.gpio" +PKG_DESC[rpi-gpio]="Python library for Raspberry Pi GPIO control [APT]" +PKG_METHOD[rpi-gpio]="apt" +PKG_CATEGORY[rpi-gpio]="sbc" + +PACKAGES[gpiozero]="python3-gpiozero" +PKG_DESC[gpiozero]="Simple GPIO library for Raspberry Pi [APT]" +PKG_METHOD[gpiozero]="apt" +PKG_CATEGORY[gpiozero]="sbc" + +# Firmware and bootloader tools +PACKAGES[dfu-util]="dfu-util" +PKG_DESC[dfu-util]="Device Firmware Upgrade utilities [APT]" +PKG_METHOD[dfu-util]="apt" +PKG_CATEGORY[dfu-util]="sbc" + +PACKAGES[stlink-tools]="stlink-tools" +PKG_DESC[stlink-tools]="STMicroelectronics STLink tools [APT]" +PKG_METHOD[stlink-tools]="apt" +PKG_CATEGORY[stlink-tools]="sbc" + +PACKAGES[avrdude]="avrdude" +PKG_DESC[avrdude]="AVR microcontroller programmer [APT]" +PKG_METHOD[avrdude]="apt" +PKG_CATEGORY[avrdude]="sbc" + +# Circuit simulation and design +PACKAGES[kicad]="kicad" +PKG_DESC[kicad]="Electronic schematic and PCB design suite [APT]" +PKG_METHOD[kicad]="apt" +PKG_CATEGORY[kicad]="sbc" + +PACKAGES[fritzing]="fritzing" +PKG_DESC[fritzing]="Electronic prototyping platform [APT]" +PKG_METHOD[fritzing]="apt" +PKG_CATEGORY[fritzing]="sbc" + +# IoT and networking tools +PACKAGES[mosquitto-clients]="mosquitto-clients" +PKG_DESC[mosquitto-clients]="MQTT client tools [APT]" +PKG_METHOD[mosquitto-clients]="apt" +PKG_CATEGORY[mosquitto-clients]="sbc" + +PACKAGES[node-red]="node-red" +PKG_DESC[node-red]="Flow-based programming for IoT [NPM]" +PKG_METHOD[node-red]="npm" +PKG_CATEGORY[node-red]="sbc" + # ============================================================================== # ALTERNATIVE INSTALLATION METHODS # ============================================================================== @@ -2329,10 +2504,6 @@ PKG_DESC[webcamize]="Webcam effects and virtual camera tool [APT]" PKG_METHOD[webcamize]="apt" PKG_CATEGORY[webcamize]="graphics" -PACKAGES[durdraw]="durdraw" -PKG_DESC[durdraw]="ASCII art drawing and animation tool [PIP]" -PKG_METHOD[durdraw]="pip" -PKG_CATEGORY[durdraw]="graphics" PACKAGES[pastel]="pastel" PKG_DESC[pastel]="Command-line tool for color manipulation and palette generation [APT]" @@ -2393,8 +2564,8 @@ PKG_METHOD[ag]="apt" PKG_CATEGORY[ag]="utilities" PACKAGES[thefuck]="thefuck" -PKG_DESC[thefuck]="Corrects errors in previous console commands [PIP]" -PKG_METHOD[thefuck]="pip" +PKG_DESC[thefuck]="Corrects errors in previous console commands [APT]" +PKG_METHOD[thefuck]="apt" PKG_CATEGORY[thefuck]="utilities" PACKAGES[lazygit]="lazygit" @@ -3874,6 +4045,7 @@ CATEGORIES=( "terminals:Terminals" "gaming-platforms:Gaming Platforms" "gaming-emulators:Gaming Emulators" + "sbc:Single Board Computers & Microcontrollers" ) # Category hotkey mappings (A-Z) @@ -3903,7 +4075,8 @@ declare -A CATEGORY_HOTKEYS=( ["terminals"]="W" ["gaming-platforms"]="X" ["gaming-emulators"]="Y" - # Note: Z is reserved for back navigation in submenus + ["sbc"]="Z" + # Note: Back navigation in submenus now uses 'B' or 'Esc' ) # ============================================================================== @@ -3949,6 +4122,59 @@ remove_apt_package() { fi } +# Ensure snapd service and CLI are ready before any snap installation +ensure_snapd_ready() { + log "Ensuring snapd is installed and ready" + if ! dpkg-query -W snapd &>/dev/null; then + log "snapd not installed; installing via APT" + apt_update + if ! sudo apt-get install -y --no-install-recommends snapd 2>&1 | tee -a "$LOGFILE"; then + log_error "Failed to install snapd" + return 1 + fi + fi + + # Ensure key services and socket are enabled and running + if command -v systemctl &>/dev/null; then + sudo systemctl enable --now snapd.socket 2>/dev/null || true + sudo systemctl enable --now snapd 2>/dev/null || true + # AppArmor is commonly required for snaps on Ubuntu + sudo systemctl enable --now apparmor 2>/dev/null || true + fi + + # Ensure /snap symlink exists on systems where it's missing + if [[ ! -e /snap ]]; then + sudo ln -sf /var/lib/snapd/snap /snap 2>/dev/null || true + fi + + # Wait briefly for snap daemon to be ready + if command -v snap &>/dev/null; then + sudo snap wait system seed.loaded 2>/dev/null || sleep 2 + snap version 2>/dev/null | tee -a "$LOGFILE" || true + fi + log "snapd is ready" +} + +requires_classic_snap() { + local pkg="$1" + if [[ -z "$pkg" ]]; then + return 1 + fi + # Known snaps that use classic confinement (common IDEs/editors) + case "$pkg" in + code|code-insiders|ghostty|sublime-text|pycharm-community|pycharm-professional|intellij-idea-community|intellij-idea-ultimate|webstorm|clion|goland|rider|rubymine|datagrip|phpstorm|android-studio|flutter) + return 0 + ;; + esac + # Dynamic check via snap info when available + if command -v snap &>/dev/null; then + if snap info "$pkg" 2>/dev/null | awk -F: '/^[[:space:]]*confinement/{print $2}' | grep -qi 'classic'; then + return 0 + fi + fi + return 1 +} + install_snap_package() { local pkg="$1" log "Installing $pkg via Snap..." @@ -3966,19 +4192,15 @@ install_snap_package() { log "Warning: Snap system not fully ready, proceeding anyway..." } - # Packages that require classic confinement - case "$pkg" in - ghostty) - log "Installing $pkg with classic confinement..." - timeout 300 sudo snap install "$pkg" --classic 2>&1 | tee -a "$LOGFILE" - local exit_code=${PIPESTATUS[0]} - ;; - *) - log "Installing $pkg..." - timeout 300 sudo snap install "$pkg" 2>&1 | tee -a "$LOGFILE" - local exit_code=${PIPESTATUS[0]} - ;; - esac + if requires_classic_snap "$pkg"; then + log "Installing $pkg with classic confinement..." + timeout 300 sudo snap install "$pkg" --classic 2>&1 | tee -a "$LOGFILE" + local exit_code=${PIPESTATUS[0]} + else + log "Installing $pkg..." + timeout 300 sudo snap install "$pkg" 2>&1 | tee -a "$LOGFILE" + local exit_code=${PIPESTATUS[0]} + fi # Check installation result if [ $exit_code -eq 124 ]; then @@ -4036,26 +4258,16 @@ remove_npm_package() { install_pip_package() { local pkg="$1" - log "Installing $pkg via pip..." - - # Ensure Python3 and pip are installed - if ! command -v pip3 >/dev/null 2>&1; then - ui_msg "Python3 Required" "Installing Python3 and pip first...\n\n⏳ This process is automatic - please wait..." - apt_update - install_apt_package "python3-pip" - if [[ $? -ne 0 ]]; then - log_error "Failed to install Python3/pip prerequisite" - return 1 - fi - fi - - pip3 install --user "$pkg" 2>&1 | tee -a "$LOGFILE" + log "Pip installations are disabled by policy" + ui_msg "Pip Install Disabled" "Installing $pkg via pip is disabled. Please use APT, SNAP, FLATPAK, or a supported method." + return 1 } remove_pip_package() { local pkg="$1" - log "Removing $pkg via pip..." - pip3 uninstall -y "$pkg" 2>/dev/null || true + log "Pip removals are disabled by policy" + ui_msg "Pip Removal Disabled" "Removing $pkg via pip is disabled. Use the appropriate package manager if installed there." + return 1 } install_cargo_package() { @@ -4097,6 +4309,20 @@ install_docker() { # Remove old versions sudo apt-get remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true + # Neutralize duplicate sources/keys that may conflict + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'download\.docker\.com/linux/ubuntu' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Docker source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ docker ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + # Add Docker repository sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg @@ -4136,6 +4362,12 @@ remove_docker() { sudo rm -f /etc/apt/sources.list.d/docker.list sudo rm -f /etc/apt/keyrings/docker.gpg + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ docker ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_minikube() { @@ -4234,6 +4466,21 @@ remove_lazydocker() { install_nodejs() { log "Installing Node.js ${NODE_LTS} from NodeSource..." + # Neutralize duplicate sources/keys that may conflict + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'deb\.nodesource\.com' "$f"; then + sudo rm -f "$f" + log "Removed conflicting NodeSource source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ nodesource ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + sudo rm -f /usr/share/keyrings/nodesource.gpg /etc/apt/keyrings/nodesource.gpg + curl -fsSL https://deb.nodesource.com/setup_${NODE_LTS}.x | sudo -E bash - apt_update install_apt_package "nodejs" @@ -4244,11 +4491,34 @@ install_nodejs() { remove_nodejs() { remove_apt_package "nodejs" sudo rm -f /etc/apt/sources.list.d/nodesource.list + sudo rm -f /usr/share/keyrings/nodesource.gpg /etc/apt/keyrings/nodesource.gpg + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ nodesource ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_vscode() { log "Installing VS Code from Microsoft repository..." + # Neutralize duplicate sources/keys that may conflict + # Remove any vendor key in trusted.gpg.d + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ microsoft ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + # Remove any existing sources pointing to the Microsoft Code repo + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'packages\.microsoft\.com/.*/code' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Microsoft source list: $f" + fi + done + wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /usr/share/keyrings/packages.microsoft.gpg > /dev/null echo "deb [arch=amd64,arm64,armhf signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | sudo tee /etc/apt/sources.list.d/vscode.list @@ -4262,11 +4532,32 @@ remove_vscode() { remove_apt_package "code" sudo rm -f /etc/apt/sources.list.d/vscode.list sudo rm -f /usr/share/keyrings/packages.microsoft.gpg + # Clean up any vendor key left in trusted.gpg.d + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ microsoft ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_brave() { log "Installing Brave browser..." + # Neutralize duplicate sources/keys that may conflict + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ brave ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'brave-browser-apt-release\.s3\.brave\.com' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Brave source list: $f" + fi + done + sudo 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 @@ -4280,11 +4571,38 @@ remove_brave() { remove_apt_package "brave-browser" sudo rm -f /etc/apt/sources.list.d/brave-browser-release.list sudo rm -f /usr/share/keyrings/brave-browser-archive-keyring.gpg + # Clean up any vendor key or duplicate sources + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ brave ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'brave-browser-apt-release\.s3\.brave\.com' "$f"; then + sudo rm -f "$f" + log "Removed Brave source list: $f" + fi + done } install_sublime() { log "Installing Sublime Text..." + # Neutralize duplicate sources/keys that may conflict + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ sublime ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'download\.sublimetext\.com' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Sublime source list: $f" + fi + done + wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/sublimehq-archive.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/sublimehq-archive.gpg] https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list @@ -4298,23 +4616,45 @@ remove_sublime() { remove_apt_package "sublime-text" sudo rm -f /etc/apt/sources.list.d/sublime-text.list sudo rm -f /usr/share/keyrings/sublimehq-archive.gpg + # Clean up any vendor key + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ sublime ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_warp() { log "Installing Warp terminal..." - # Add Warp's official GPG key and repository + # Normalize/clean any conflicting Warp repo entries and keys first + # Remove vendor key that may conflict with our canonical keyring path + if [[ -f "/etc/apt/trusted.gpg.d/warpdotdev.gpg" ]]; then + sudo rm -f "/etc/apt/trusted.gpg.d/warpdotdev.gpg" + log "Removed vendor key: /etc/apt/trusted.gpg.d/warpdotdev.gpg" + fi + + # Remove any existing sources that point to releases.warp.dev to avoid duplicate Signed-By entries + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'releases\.warp\.dev/.*/deb' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Warp source list: $f" + fi + done + + # Add Warp's official GPG key and our canonical repository entry sudo mkdir -p /usr/share/keyrings if ! wget -qO- https://releases.warp.dev/linux/keys/warp.asc | sudo gpg --dearmor -o /usr/share/keyrings/warp-archive-keyring.gpg; then log_error "Failed to add Warp GPG key" return 1 fi - - # Add Warp repository + echo "deb [signed-by=/usr/share/keyrings/warp-archive-keyring.gpg] https://releases.warp.dev/linux/deb stable main" | sudo tee /etc/apt/sources.list.d/warp-terminal.list > /dev/null - + log "Added Warp source: /etc/apt/sources.list.d/warp-terminal.list" + # Update package list and install - sudo apt-get update -qq + apt_update if install_apt_package "warp-terminal"; then ui_msg "Warp Installed" "Warp terminal installed successfully." else @@ -4325,8 +4665,15 @@ install_warp() { remove_warp() { remove_apt_package "warp-terminal" - sudo rm -f /etc/apt/sources.list.d/warp-terminal.list + # Clean up both our canonical repo and any vendor leftovers + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'releases\.warp\.dev/.*/deb' "$f"; then + sudo rm -f "$f" + log "Removed Warp source list: $f" + fi + done sudo rm -f /usr/share/keyrings/warp-archive-keyring.gpg + sudo rm -f /etc/apt/trusted.gpg.d/warpdotdev.gpg } install_ollama() { @@ -4352,25 +4699,25 @@ remove_ollama() { } install_yt-dlp() { - log "Installing yt-dlp..." - - # Install via pip for latest version - if command -v pip3 >/dev/null 2>&1; then - if pip3 install --user yt-dlp; then - ui_msg "yt-dlp Installed" "yt-dlp YouTube downloader installed successfully." - else - log_error "Failed to install yt-dlp via pip" - return 1 - fi + log "Installing yt-dlp via apt..." + if sudo apt-get update && sudo apt-get install -y yt-dlp; then + ui_msg "yt-dlp Installed" "yt-dlp YouTube downloader installed successfully via apt." + return 0 else - log_error "pip3 not found. Please install Python3 and pip first." + log_error "Failed to install yt-dlp via apt" return 1 fi } remove_yt-dlp() { - log "Removing yt-dlp..." - pip3 uninstall -y yt-dlp 2>/dev/null || true + log "Removing yt-dlp via apt..." + if sudo apt-get remove -y yt-dlp; then + ui_msg "yt-dlp Removed" "yt-dlp removed successfully via apt." + return 0 + else + log_error "Failed to remove yt-dlp via apt" + return 1 + fi } install_invidious() { @@ -4567,6 +4914,20 @@ remove_discord-deb() { install_plex() { log "Installing Plex Media Server..." + # Neutralize duplicate sources/keys + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'downloads\.plex\.tv/repo/deb' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Plex source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ plex ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + # Add Plex repository key curl https://downloads.plex.tv/plex-keys/PlexSign.key | sudo apt-key add - @@ -4679,6 +5040,8 @@ remove_lm-studio() { install_text-generation-webui() { log "Installing Text Generation WebUI..." + ui_msg "Python/Pip Disabled" "Text Generation WebUI requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 # Clone the repository local install_dir="/opt/text-generation-webui" @@ -4755,6 +5118,8 @@ remove_whisper-cpp() { install_comfyui() { log "Installing ComfyUI..." + ui_msg "Python/Pip Disabled" "ComfyUI requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 # Clone ComfyUI repository local install_dir="/opt/ComfyUI" @@ -4789,6 +5154,8 @@ remove_comfyui() { install_invokeai() { log "Installing InvokeAI..." + ui_msg "Python/Pip Disabled" "InvokeAI requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 # Install InvokeAI via pip if pip3 install invokeai[xformers] --upgrade 2>/dev/null; then @@ -5281,6 +5648,20 @@ EOF install_bruno() { log "Installing Bruno API client..." + # Neutralize duplicate sources/keys that may conflict + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'debian\.usebruno\.com' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Bruno source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ bruno|usebruno ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + # Create keyrings directory sudo mkdir -p /etc/apt/keyrings @@ -5312,6 +5693,12 @@ remove_bruno() { # Remove repository and key sudo rm -f /etc/apt/sources.list.d/bruno.list sudo rm -f /etc/apt/keyrings/bruno.gpg + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ bruno|usebruno ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done # Update package list sudo apt update @@ -5357,6 +5744,20 @@ remove_yaak() { install_caddy() { log "Installing Caddy web server..." + # Neutralize duplicate sources/keys + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'dl\.cloudsmith\.io/public/caddy/stable' "$f"; then + sudo rm -f "$f" + log "Removed conflicting Caddy source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ caddy|cloudsmith ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + # Install dependencies sudo apt update sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https @@ -5378,6 +5779,12 @@ remove_caddy() { remove_apt_package "caddy" sudo rm -f /etc/apt/sources.list.d/caddy-stable.list sudo rm -f /usr/share/keyrings/caddy-stable-archive-keyring.gpg + for k in /etc/apt/trusted.gpg.d/*.gpg; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ caddy|cloudsmith ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_ngrok() { @@ -5391,10 +5798,26 @@ install_ngrok() { *) log_error "Unsupported architecture: $arch"; return 1 ;; esac - # Download and install ngrok - curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null - echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list - sudo apt update + # Neutralize duplicate sources/keys that may conflict + for f in /etc/apt/sources.list.d/*.list; do + if [[ -f "$f" ]] && grep -qE 'ngrok-agent\.s3\.amazonaws\.com' "$f"; then + sudo rm -f "$f" + log "Removed conflicting ngrok source list: $f" + fi + done + for k in /etc/apt/trusted.gpg.d/*; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ ngrok ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done + + # Add canonical keyring and repository with signed-by + sudo mkdir -p /usr/share/keyrings + curl -fsSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo gpg --dearmor -o /usr/share/keyrings/ngrok-archive-keyring.gpg + echo "deb [signed-by=/usr/share/keyrings/ngrok-archive-keyring.gpg] https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list + + apt_update install_apt_package "ngrok" ui_msg "ngrok Installed" "ngrok installed successfully. Run 'ngrok config add-authtoken ' to authenticate." @@ -5403,7 +5826,13 @@ install_ngrok() { remove_ngrok() { remove_apt_package "ngrok" sudo rm -f /etc/apt/sources.list.d/ngrok.list - sudo rm -f /etc/apt/trusted.gpg.d/ngrok.asc + sudo rm -f /usr/share/keyrings/ngrok-archive-keyring.gpg + for k in /etc/apt/trusted.gpg.d/*; do + if [[ -f "$k" ]] && [[ "$(basename "$k")" =~ ngrok ]]; then + sudo rm -f "$k" + log "Removed vendor key: $k" + fi + done } install_mailhog() { @@ -5478,6 +5907,8 @@ remove_rustup() { # AI Image Generation Tools install_automatic1111() { log "Installing Automatic1111 Stable Diffusion WebUI..." + ui_msg "Python/Pip Disabled" "Automatic1111 requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 local install_dir="$HOME/automatic1111" @@ -5513,6 +5944,8 @@ remove_automatic1111() { install_fooocus() { log "Installing Fooocus Stable Diffusion frontend..." + ui_msg "Python/Pip Disabled" "Fooocus requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 local install_dir="$HOME/fooocus" @@ -5550,6 +5983,8 @@ remove_fooocus() { install_sd-next() { log "Installing SD.Next (Stable Diffusion Next)..." + ui_msg "Python/Pip Disabled" "SD.Next requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 local install_dir="$HOME/sd-next" @@ -5585,6 +6020,8 @@ remove_sd-next() { install_kohya-ss-gui() { log "Installing Kohya-ss GUI for model training..." + ui_msg "Python/Pip Disabled" "Kohya-ss GUI requires Python/pip which are disabled by policy. Please use a non-Python alternative." + return 1 local install_dir="$HOME/kohya-ss" @@ -5618,6 +6055,8 @@ remove_kohya-ss-gui() { rm -f ~/.local/share/applications/kohya-ss.desktop } + + # Shell Enhancement Tools install_oh-my-zsh() { log "Installing Oh My Zsh..." @@ -5760,28 +6199,25 @@ remove_zoxide() { } install_poetry() { - log "Installing Poetry Python dependency manager..." - - # Install via pip3 - if pip3 install --user poetry; then - # Add to PATH if not already there - if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then - echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc - echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc 2>/dev/null || true - fi - ui_msg "Poetry Installed" "Poetry Python dependency manager installed successfully via pip3." + log "Installing Poetry via apt..." + if sudo apt-get update && sudo apt-get install -y python3-poetry; then + ui_msg "Poetry Installed" "Poetry installed successfully via apt." return 0 else - log_error "Failed to install Poetry" + log_error "Failed to install Poetry via apt" return 1 fi } remove_poetry() { - log "Removing Poetry..." - pip3 uninstall -y poetry 2>/dev/null || true - log "Poetry removed successfully" - return 0 + log "Removing Poetry via apt..." + if sudo apt-get remove -y python3-poetry; then + log "Poetry removed successfully via apt" + return 0 + else + log_error "Failed to remove Poetry via apt" + return 1 + fi } install_deno() { @@ -5842,23 +6278,25 @@ remove_bun() { } install_mkdocs() { - log "Installing MkDocs static site generator..." - - # Install via pip3 - if pip3 install --user mkdocs mkdocs-material; then - ui_msg "MkDocs Installed" "MkDocs static site generator installed successfully via pip3." + log "Installing MkDocs via apt..." + if sudo apt-get update && sudo apt-get install -y mkdocs; then + ui_msg "MkDocs Installed" "MkDocs installed successfully via apt." return 0 else - log_error "Failed to install MkDocs" + log_error "Failed to install MkDocs via apt" return 1 fi } remove_mkdocs() { - log "Removing MkDocs..." - pip3 uninstall -y mkdocs mkdocs-material 2>/dev/null || true - log "MkDocs removed successfully" - return 0 + log "Removing MkDocs via apt..." + if sudo apt-get remove -y mkdocs; then + log "MkDocs removed successfully via apt" + return 0 + else + log_error "Failed to remove MkDocs via apt" + return 1 + fi } install_insomnia() { @@ -6240,6 +6678,200 @@ install_dog() { fi } +# ============================================================================== +# SBC/MICROCONTROLLER CUSTOM INSTALLERS +# ============================================================================== + +install_balena-etcher() { + log "Installing balenaEtcher..." + + local arch + case "$(uname -m)" in + x86_64) arch="x64" ;; + aarch64|arm64) arch="arm64" ;; + *) log_error "Unsupported architecture"; return 1 ;; + esac + + local latest_url=$(curl -s https://api.github.com/repos/balena-io/etcher/releases/latest | grep "browser_download_url.*${arch}.deb" | cut -d '"' -f 4) + + if [[ -z "$latest_url" ]]; then + log_error "Failed to get balenaEtcher download URL" + return 1 + fi + + local temp_file="/tmp/balena-etcher.deb" + + if wget -O "$temp_file" "$latest_url" 2>/dev/null; then + sudo dpkg -i "$temp_file" || sudo apt-get install -f -y + rm -f "$temp_file" + log "balenaEtcher installed successfully" + return 0 + else + log_error "Failed to download balenaEtcher" + return 1 + fi +} + +remove_balena-etcher() { + remove_apt_package "balena-etcher-electron" +} + +install_arduino-ide() { + log "Installing Arduino IDE 2.0..." + + local arch + case "$(uname -m)" in + x86_64) arch="Linux_64bit" ;; + aarch64|arm64) arch="Linux_ARM64" ;; + *) log_error "Unsupported architecture"; return 1 ;; + esac + + local latest_url=$(curl -s https://api.github.com/repos/arduino/arduino-ide/releases/latest | grep "browser_download_url.*${arch}.zip" | cut -d '"' -f 4) + + if [[ -z "$latest_url" ]]; then + log_error "Failed to get Arduino IDE download URL" + return 1 + fi + + local temp_file="/tmp/arduino-ide.zip" + local install_dir="/opt/arduino-ide" + + if wget -O "$temp_file" "$latest_url" 2>/dev/null; then + sudo rm -rf "$install_dir" + sudo mkdir -p "$install_dir" + sudo unzip -q "$temp_file" -d "$install_dir" --strip-components=1 + sudo chmod +x "$install_dir/arduino-ide" + + # Create desktop entry + sudo tee /usr/share/applications/arduino-ide.desktop > /dev/null </dev/null; then + tar -xzf "$temp_file" -C /tmp/ + sudo mv /tmp/arduino-cli /usr/local/bin/ + sudo chmod +x /usr/local/bin/arduino-cli + rm -f "$temp_file" + log "Arduino CLI installed successfully" + return 0 + else + log_error "Failed to download Arduino CLI" + return 1 + fi +} + +remove_arduino-cli() { + sudo rm -f /usr/local/bin/arduino-cli +} + +install_esp-idf() { + log "Installing ESP-IDF..." + + local idf_path="$HOME/esp/esp-idf" + + # Install prerequisites + install_apt_package "git" + install_apt_package "wget" + install_apt_package "flex" + install_apt_package "bison" + install_apt_package "gperf" + install_apt_package "python3" + install_apt_package "python3-pip" + install_apt_package "python3-venv" + install_apt_package "cmake" + install_apt_package "ninja-build" + install_apt_package "ccache" + install_apt_package "libffi-dev" + install_apt_package "libssl-dev" + install_apt_package "dfu-util" + install_apt_package "libusb-1.0-0" + + # Clone ESP-IDF + mkdir -p "$HOME/esp" + if [[ -d "$idf_path" ]]; then + cd "$idf_path" + git pull + else + git clone --recursive https://github.com/espressif/esp-idf.git "$idf_path" + fi + + cd "$idf_path" + ./install.sh esp32 + + # Add to shell profile + local shell_rc="$HOME/.bashrc" + [[ "$SHELL" == *"zsh"* ]] && shell_rc="$HOME/.zshrc" + + if ! grep -q "esp-idf/export.sh" "$shell_rc" 2>/dev/null; then + echo "# ESP-IDF" >> "$shell_rc" + echo "alias get_idf='. $idf_path/export.sh'" >> "$shell_rc" + fi + + ui_msg "ESP-IDF Installed" "ESP-IDF installed successfully. Run 'get_idf' to set up the environment, or restart your terminal." +} + +remove_esp-idf() { + if ui_yesno "Remove ESP-IDF?" "This will remove ESP-IDF installation. Continue?"; then + rm -rf "$HOME/esp/esp-idf" + + # Remove from shell profile + local shell_rc="$HOME/.bashrc" + [[ "$SHELL" == *"zsh"* ]] && shell_rc="$HOME/.zshrc" + + if [[ -f "$shell_rc" ]]; then + sed -i '/# ESP-IDF/d' "$shell_rc" + sed -i '/get_idf=/d' "$shell_rc" + fi + + log "ESP-IDF removed" + fi +} + # ============================================================================== # BUNTAGE MANAGEMENT DISPATCHER # ============================================================================== @@ -6267,6 +6899,10 @@ install_package() { install_result=$? ;; snap) + ensure_snapd_ready || { + ui_msg "snapd Not Ready" "snapd could not be prepared. Check logs at $LOGFILE." + install_result=1 + } install_snap_package "$pkg" install_result=$? ;; @@ -6275,8 +6911,8 @@ install_package() { install_result=$? ;; pip) - install_pip_package "$pkg" - install_result=$? + ui_msg "Pip Install Disabled" "Installing $name via pip is disabled. Please use APT, SNAP, FLATPAK, or a supported method." + install_result=1 ;; cargo) install_cargo_package "$pkg" @@ -6353,6 +6989,10 @@ install_package() { cheat) install_cheat; install_result=$? ;; broot) install_broot; install_result=$? ;; dog) install_dog; install_result=$? ;; + balena-etcher) install_balena-etcher; install_result=$? ;; + arduino-ide) install_arduino-ide; install_result=$? ;; + arduino-cli) install_arduino-cli; install_result=$? ;; + esp-idf) install_esp-idf; install_result=$? ;; *) log_error "Unknown custom installer: $name"; install_result=1 ;; esac ;; @@ -6453,6 +7093,10 @@ remove_package() { mergerfs) remove_mergerfs; remove_result=$? ;; plex) remove_plex; remove_result=$? ;; ums) remove_ums; remove_result=$? ;; + balena-etcher) remove_balena-etcher; remove_result=$? ;; + arduino-ide) remove_arduino-ide; remove_result=$? ;; + arduino-cli) remove_arduino-cli; remove_result=$? ;; + esp-idf) remove_esp-idf; remove_result=$? ;; *) log_error "Unknown custom remover: $name"; remove_result=1 ;; esac ;; @@ -6646,19 +7290,6 @@ show_category_menu() { local -A last_hotkey_selection local -A hotkey_categories - # Build reverse mapping of hotkeys to categories for cycling - for cat_id in "${!CATEGORY_HOTKEYS[@]}"; do - local hotkey="${CATEGORY_HOTKEYS[$cat_id]}" - if [[ -n "${hotkey_categories[$hotkey]:-}" ]]; then - hotkey_categories[$hotkey]="${hotkey_categories[$hotkey]} $cat_id" - else - hotkey_categories[$hotkey]="$cat_id" - fi - done - - # Add special Q hotkey handling for communication and quit - hotkey_categories["Q"]="communication quit" - # Start background cache refresh on first load refresh_cache_silent @@ -6668,9 +7299,12 @@ show_category_menu() { # System management tools at the top log "Adding system management menu items..." + menu_items+=("cloudflare-setup" "(.Y.) Cloudflare Firewall Setup") + menu_items+=("essentials-setup" "(.Y.) First-Time Essentials Setup") menu_items+=("system-info" "(.Y.) System Information") menu_items+=("mirror-management" "(.Y.) Mirror Management") menu_items+=("database-management" "(.Y.) Database Management") + menu_items+=("swappiness" "(.Y.) Swappiness Tuning") menu_items+=("wordpress-setup" "(.Y.) WordPress Management") menu_items+=("keyboard-layout" "(.Y.) Keyboard Layout Configuration") menu_items+=("php-settings" "(.Y.) PHP Configuration") @@ -6680,59 +7314,108 @@ show_category_menu() { menu_items+=("" "(_*_)") log "Processing categories array with ${#CATEGORIES[@]} entries" + # Build sortable array as name:id:installed:total for alphabetical ordering by name + local sortable_categories=() for entry in "${CATEGORIES[@]}"; do local cat_id="${entry%%:*}" local cat_name="${entry#*:}" - + # Skip excluded categories if [[ -n "${EXCLUDED_CATEGORIES[$cat_id]:-}" ]]; then log "Skipping excluded category: $cat_id" continue fi - + # Count installed buntages in category local total=0 local installed=0 - + # Use a safer iteration method local package_names=() for name in "${!PACKAGES[@]}"; do package_names+=("$name") done - + for name in "${package_names[@]}"; do local pkg_category="${PKG_CATEGORY[$name]:-}" - if [[ "$pkg_category" == "$cat_id" ]]; then total=$((total + 1)) - - # Re-enable installation check with silent mode if is_package_installed "$name" "true"; then installed=$((installed + 1)) fi fi done - - # Add refresh indicator if cache is still updating - local refresh_indicator="" - if ! is_cache_refresh_complete; then - refresh_indicator=" 🔄" + + sortable_categories+=("$cat_name:$cat_id:$installed:$total") + done + + # Sort categories alphabetically by display name + if [[ ${#sortable_categories[@]} -gt 0 ]]; then + mapfile -t sortable_categories < <(printf "%s\n" "${sortable_categories[@]}" | sort) + fi + + # Build dynamic hotkeys based on an allowed set, skipping reserved letters + # Reserved: Q (Quit), Z (Back) + local -A dynamic_hotkeys=() + # Reset hotkey_categories for this render + hotkey_categories=() + + local allowed_upper=(A B C D E F G H I J K L M N O P R S T U V W X Y) + local allowed_lower=(a b c d e f g h i j k l m n o p r s t u v w x y) + local index=0 + for item in "${sortable_categories[@]}"; do + local rest="${item#*:}" + local cat_id="${rest%%:*}" + local letter="" + if (( index < ${#allowed_upper[@]} )); then + letter="${allowed_upper[$index]}" + else + local lower_index=$((index - ${#allowed_upper[@]})) + if (( lower_index < ${#allowed_lower[@]} )); then + letter="${allowed_lower[$lower_index]}" + else + # Fallback: cycle through allowed_upper again to ensure a tag exists + letter="${allowed_upper[$((index % ${#allowed_upper[@]}))]}" + fi fi - - # Get hotkey for this category - local hotkey="${CATEGORY_HOTKEYS[$cat_id]:-}" + dynamic_hotkeys[$cat_id]="$letter" + hotkey_categories[$letter]="$cat_id" + index=$((index + 1)) + done + + # Add refresh indicator if cache is still updating + local refresh_indicator="" + if ! is_cache_refresh_complete; then + refresh_indicator=" 🔄" + fi + + # Rebuild menu_items from sorted categories + for item in "${sortable_categories[@]}"; do + local cat_name="${item%%:*}" + local rest="${item#*:}" + local cat_id="${rest%%:*}" + local installed_total="${rest#*:}" + local installed="${installed_total%%:*}" + local total="${installed_total#*:}" + + # Get dynamic hotkey for this category (A..Z based on sorted order) + local hotkey="${dynamic_hotkeys[$cat_id]:-}" local hotkey_display="" if [[ -n "$hotkey" ]]; then hotkey_display="($hotkey) " fi - - menu_items+=("$cat_id" "${hotkey_display}$cat_name [$installed/$total installed]$refresh_indicator") + + # Use the hotkey letter as the menu tag so typing the letter jumps + # directly to the row that displays that bracketed hotkey. + # The actual category id is recovered later via hotkey_categories. + menu_items+=("$hotkey" "${hotkey_display}$cat_name [$installed/$total installed]$refresh_indicator") done log "Adding additional menu items..." # System management tools at the top menu_items+=("" "(_*_)") - menu_items+=("quit" "(Q) Exit Installer") + # Map Quit to the displayed hotkey letter so pressing 'Q' jumps/selects it + menu_items+=("Q" "(Q) Exit Installer") log "Calling ui_menu with ${#menu_items[@]} menu items" log "Menu items array contents: ${menu_items[*]}" @@ -6752,9 +7435,21 @@ show_category_menu() { fi case "$choice" in - quit|back|q) + cloudflare-setup) + show_cloudflare_setup_menu || { + log "ERROR: show_cloudflare_setup_menu failed" + ui_msg "Error" "Failed to display Cloudflare setup menu. Please check the logs." + } + ;; + essentials-setup) + show_essentials_setup_menu || { + log "ERROR: show_essentials_setup_menu failed" + ui_msg "Error" "Failed to run First-Time Essentials setup. Please check the logs." + } + ;; + quit|back|q|Q) # Simple confirmation for quit - if [[ "$choice" == "quit" || "$choice" == "q" ]]; then + if [[ "$choice" == "quit" || "$choice" == "q" || "$choice" == "Q" ]]; then if ui_yesno "Confirm Exit" "Are you sure you want to exit Ultrabunt?"; then break else @@ -6806,6 +7501,12 @@ show_category_menu() { ui_msg "Error" "Failed to display database management menu. Please check the logs." } ;; + swappiness) + show_swappiness_menu || { + log "ERROR: show_swappiness_menu failed" + ui_msg "Error" "Failed to display Swappiness tuning menu. Please check the logs." + } + ;; log-viewer) show_log_viewer_menu || { log "ERROR: show_log_viewer_menu failed" @@ -6819,7 +7520,7 @@ show_category_menu() { continue ;; *) - # Handle A-Z hotkey cycling + # Handle A-Z hotkey selection local upper_choice="${choice^^}" # Convert to uppercase # Check if it's a valid hotkey (A-Y, excluding Z which is for back navigation) @@ -6943,7 +7644,7 @@ show_buntage_list() { menu_items=("${sorted_items[@]}") menu_items+=("" "(_*_)") - menu_items+=("zback" "(Z) ← Back to Categories") + menu_items+=("zback" "(B) ← Back to Categories") local choice choice=$(ui_menu "$cat_name" \ @@ -6977,6 +7678,11 @@ show_buntage_actions() { while true; do local info="Buntage: $name\nDescription: $desc\nInstall Method: $method\nBuntage Name: $pkg\nStatus: $status" + if [[ "$method" == "snap" ]]; then + if requires_classic_snap "$pkg"; then + info+="\nNote: Snap uses classic confinement and will install with --classic" + fi + fi local menu_items=() @@ -6989,7 +7695,7 @@ show_buntage_actions() { menu_items+=("info" "(.Y.) Show Detailed Info") fi - menu_items+=("zback" "(Z) ← Back to Buntage List") + menu_items+=("zback" "(B) ← Back to Buntage List") local choice choice=$(ui_menu "Manage: $name" "$info" $DIALOG_HEIGHT $DIALOG_WIDTH $DIALOG_MENU_HEIGHT "${menu_items[@]}") @@ -7340,10 +8046,698 @@ show_terminal_info() { ui_info "Terminal Information" "$info" } -show_system_info() { - log "Starting show_system_info function" +show_cpu_details() { + log "Starting show_cpu_details function" - local info="SYSTEM INFORMATION\n" + local info="CPU & PROCESSOR DETAILS\n" + info+="═══════════════════════════════════════\n\n" + + # Basic CPU Information + if [[ -f /proc/cpuinfo ]]; then + local cpu_model cpu_vendor cpu_family cpu_stepping cpu_microcode + cpu_model=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_model="Unknown" + cpu_vendor=$(grep "vendor_id" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_vendor="Unknown" + cpu_family=$(grep "cpu family" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_family="Unknown" + cpu_stepping=$(grep "stepping" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_stepping="Unknown" + cpu_microcode=$(grep "microcode" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_microcode="Unknown" + + info+="Model: $cpu_model\n" + info+="Vendor: $cpu_vendor\n" + info+="Family: $cpu_family\n" + info+="Stepping: $cpu_stepping\n" + info+="Microcode: $cpu_microcode\n\n" + fi + + # Core and Thread Information + local physical_cores logical_cores threads_per_core + physical_cores=$(grep "cpu cores" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || physical_cores="Unknown" + logical_cores=$(nproc 2>/dev/null) || logical_cores="Unknown" + if [[ "$physical_cores" != "Unknown" && "$logical_cores" != "Unknown" ]]; then + threads_per_core=$((logical_cores / physical_cores)) + else + threads_per_core="Unknown" + fi + + info+="Physical Cores: $physical_cores\n" + info+="Logical Cores: $logical_cores\n" + info+="Threads per Core: $threads_per_core\n\n" + + # CPU Frequencies + local cpu_freq_current cpu_freq_max cpu_freq_min + if [[ -f /proc/cpuinfo ]]; then + cpu_freq_current=$(grep "cpu MHz" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_freq_current="Unknown" + [[ "$cpu_freq_current" != "Unknown" ]] && cpu_freq_current="${cpu_freq_current} MHz" + fi + + if [[ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]]; then + cpu_freq_max=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq 2>/dev/null) + [[ -n "$cpu_freq_max" ]] && cpu_freq_max="$((cpu_freq_max / 1000)) MHz" || cpu_freq_max="Unknown" + else + cpu_freq_max="Unknown" + fi + + if [[ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq ]]; then + cpu_freq_min=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq 2>/dev/null) + [[ -n "$cpu_freq_min" ]] && cpu_freq_min="$((cpu_freq_min / 1000)) MHz" || cpu_freq_min="Unknown" + else + cpu_freq_min="Unknown" + fi + + info+="Current Frequency: $cpu_freq_current\n" + info+="Maximum Frequency: $cpu_freq_max\n" + info+="Minimum Frequency: $cpu_freq_min\n\n" + + # Cache Information + if command -v lscpu &>/dev/null; then + local l1d_cache l1i_cache l2_cache l3_cache + l1d_cache=$(lscpu 2>/dev/null | grep "L1d cache:" | awk '{print $3}' 2>/dev/null) || l1d_cache="Unknown" + l1i_cache=$(lscpu 2>/dev/null | grep "L1i cache:" | awk '{print $3}' 2>/dev/null) || l1i_cache="Unknown" + l2_cache=$(lscpu 2>/dev/null | grep "L2 cache:" | awk '{print $3}' 2>/dev/null) || l2_cache="Unknown" + l3_cache=$(lscpu 2>/dev/null | grep "L3 cache:" | awk '{print $3}' 2>/dev/null) || l3_cache="Unknown" + + info+="L1 Data Cache: $l1d_cache\n" + info+="L1 Instruction Cache: $l1i_cache\n" + info+="L2 Cache: $l2_cache\n" + info+="L3 Cache: $l3_cache\n\n" + fi + + # CPU Features and Flags + if [[ -f /proc/cpuinfo ]]; then + local cpu_flags + cpu_flags=$(grep "flags" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_flags="Unknown" + if [[ "$cpu_flags" != "Unknown" ]]; then + local important_flags="" + for flag in sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2 avx512f aes pae nx lm vmx svm; do + if echo "$cpu_flags" | grep -q "$flag"; then + important_flags+="$flag " + fi + done + info+="Key Features: ${important_flags:-None detected}\n\n" + fi + fi + + # CPU Load and Temperature + local load_avg cpu_temp + load_avg=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' 2>/dev/null | sed 's/^ *//' 2>/dev/null) || load_avg="Unknown" + info+="Load Average:$load_avg\n" + + # Try to get CPU temperature + if [[ -f /sys/class/thermal/thermal_zone0/temp ]]; then + cpu_temp=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null) + if [[ -n "$cpu_temp" ]]; then + cpu_temp="$((cpu_temp / 1000))°C" + else + cpu_temp="Unknown" + fi + else + cpu_temp="Unknown" + fi + info+="Temperature: $cpu_temp\n\n" + + # Vulnerabilities + if [[ -d /sys/devices/system/cpu/vulnerabilities ]]; then + info+="SECURITY VULNERABILITIES\n" + info+="─────────────────────────────────────\n" + for vuln in /sys/devices/system/cpu/vulnerabilities/*; do + if [[ -f "$vuln" ]]; then + local vuln_name status + vuln_name=$(basename "$vuln") + status=$(cat "$vuln" 2>/dev/null) || status="Unknown" + info+="$(printf "%-20s: %s\n" "$vuln_name" "$status")" + fi + done + fi + + ui_info "CPU & Processor Details" "$info" + return 0 +} + +show_memory_details() { + log "Starting show_memory_details function" + + local info="MEMORY & RAM INFORMATION\n" + info+="═══════════════════════════════════════\n\n" + + # Basic Memory Information + if [[ -f /proc/meminfo ]]; then + local mem_total mem_free mem_available mem_used mem_cached mem_buffers + mem_total=$(grep "MemTotal:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || mem_total="0" + mem_free=$(grep "MemFree:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || mem_free="0" + mem_available=$(grep "MemAvailable:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || mem_available="0" + mem_cached=$(grep "^Cached:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || mem_cached="0" + mem_buffers=$(grep "Buffers:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || mem_buffers="0" + + if [[ "$mem_total" != "0" ]]; then + mem_used=$((mem_total - mem_available)) + local mem_total_gb mem_used_gb mem_free_gb mem_available_gb + mem_total_gb=$(echo "scale=2; $mem_total / 1024 / 1024" | bc 2>/dev/null) || mem_total_gb="Unknown" + mem_used_gb=$(echo "scale=2; $mem_used / 1024 / 1024" | bc 2>/dev/null) || mem_used_gb="Unknown" + mem_free_gb=$(echo "scale=2; $mem_free / 1024 / 1024" | bc 2>/dev/null) || mem_free_gb="Unknown" + mem_available_gb=$(echo "scale=2; $mem_available / 1024 / 1024" | bc 2>/dev/null) || mem_available_gb="Unknown" + + local usage_percent + usage_percent=$(echo "scale=1; $mem_used * 100 / $mem_total" | bc 2>/dev/null) || usage_percent="Unknown" + + info+="Total Memory: ${mem_total_gb} GB (${mem_total} KB)\n" + info+="Used Memory: ${mem_used_gb} GB (${mem_used} KB)\n" + info+="Free Memory: ${mem_free_gb} GB (${mem_free} KB)\n" + info+="Available Memory: ${mem_available_gb} GB (${mem_available} KB)\n" + info+="Memory Usage: ${usage_percent}%\n\n" + + # Cache and Buffer Information + local cached_gb buffers_gb + cached_gb=$(echo "scale=2; $mem_cached / 1024 / 1024" | bc 2>/dev/null) || cached_gb="Unknown" + buffers_gb=$(echo "scale=2; $mem_buffers / 1024 / 1024" | bc 2>/dev/null) || buffers_gb="Unknown" + + info+="Cached: ${cached_gb} GB (${mem_cached} KB)\n" + info+="Buffers: ${buffers_gb} GB (${mem_buffers} KB)\n\n" + fi + fi + + # Swap Information + if [[ -f /proc/meminfo ]]; then + local swap_total swap_free swap_used + swap_total=$(grep "SwapTotal:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || swap_total="0" + swap_free=$(grep "SwapFree:" /proc/meminfo 2>/dev/null | awk '{print $2}' 2>/dev/null) || swap_free="0" + + if [[ "$swap_total" != "0" ]]; then + swap_used=$((swap_total - swap_free)) + local swap_total_gb swap_used_gb swap_usage_percent + swap_total_gb=$(echo "scale=2; $swap_total / 1024 / 1024" | bc 2>/dev/null) || swap_total_gb="Unknown" + swap_used_gb=$(echo "scale=2; $swap_used / 1024 / 1024" | bc 2>/dev/null) || swap_used_gb="Unknown" + swap_usage_percent=$(echo "scale=1; $swap_used * 100 / $swap_total" | bc 2>/dev/null) || swap_usage_percent="Unknown" + + info+="SWAP INFORMATION\n" + info+="─────────────────────────────────────\n" + info+="Total Swap: ${swap_total_gb} GB\n" + info+="Used Swap: ${swap_used_gb} GB\n" + info+="Swap Usage: ${swap_usage_percent}%\n\n" + else + info+="SWAP INFORMATION\n" + info+="─────────────────────────────────────\n" + info+="No swap space configured\n\n" + fi + fi + + # Memory Hardware Information (DMI) + if command -v dmidecode &>/dev/null; then + info+="MEMORY HARDWARE DETAILS\n" + info+="─────────────────────────────────────\n" + + local memory_devices + memory_devices=$(dmidecode -t memory 2>/dev/null | grep -E "(Size|Speed|Type|Manufacturer|Part Number):" | head -20 2>/dev/null) + if [[ -n "$memory_devices" ]]; then + info+="$memory_devices\n\n" + else + info+="Memory hardware details not available\n\n" + fi + fi + + # Memory Usage by Process (Top 10) + info+="TOP MEMORY CONSUMING PROCESSES\n" + info+="─────────────────────────────────────\n" + local top_processes + top_processes=$(ps aux --sort=-%mem 2>/dev/null | head -11 | tail -10 | awk '{printf "%-15s %6s %s\n", $1, $4"%", $11}' 2>/dev/null) + if [[ -n "$top_processes" ]]; then + info+="USER MEM% COMMAND\n" + info+="$top_processes\n" + else + info+="Process information not available\n" + fi + + ui_info "Memory & RAM Information" "$info" + return 0 +} + +show_storage_details() { + log "Starting show_storage_details function" + + local info="STORAGE & DISK DETAILS\n" + info+="═══════════════════════════════════════\n\n" + + # Disk Usage Summary + info+="FILESYSTEM USAGE\n" + info+="─────────────────────────────────────\n" + local disk_usage + disk_usage=$(df -h 2>/dev/null | grep -E '^/dev/' | grep -v -E '(tmpfs|udev|overlay)' 2>/dev/null) + if [[ -n "$disk_usage" ]]; then + info+="FILESYSTEM SIZE USED AVAIL USE% MOUNTED ON\n" + info+="$disk_usage\n\n" + else + info+="Filesystem information not available\n\n" + fi + + # Block Devices + info+="BLOCK DEVICES\n" + info+="─────────────────────────────────────\n" + if command -v lsblk &>/dev/null; then + local block_devices + block_devices=$(lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT 2>/dev/null) + if [[ -n "$block_devices" ]]; then + info+="$block_devices\n\n" + else + info+="Block device information not available\n\n" + fi + fi + + # Disk Information + info+="PHYSICAL DISKS\n" + info+="─────────────────────────────────────\n" + if [[ -d /sys/block ]]; then + for disk in /sys/block/sd* /sys/block/nvme* /sys/block/hd*; do + if [[ -d "$disk" ]]; then + local disk_name size model + disk_name=$(basename "$disk") + + # Get size + if [[ -f "$disk/size" ]]; then + local sectors + sectors=$(cat "$disk/size" 2>/dev/null) || sectors="0" + if [[ "$sectors" != "0" ]]; then + size=$(echo "scale=2; $sectors * 512 / 1024 / 1024 / 1024" | bc 2>/dev/null) || size="Unknown" + size="${size} GB" + else + size="Unknown" + fi + else + size="Unknown" + fi + + # Get model + if [[ -f "$disk/device/model" ]]; then + model=$(cat "$disk/device/model" 2>/dev/null | tr -d ' \t\n\r' 2>/dev/null) || model="Unknown" + else + model="Unknown" + fi + + info+="/dev/$disk_name: $size ($model)\n" + fi + done + info+="\n" + fi + + # SMART Information (if available) + if command -v smartctl &>/dev/null; then + info+="SMART STATUS\n" + info+="─────────────────────────────────────\n" + for disk in /dev/sd? /dev/nvme?n?; do + if [[ -e "$disk" ]]; then + local smart_status + smart_status=$(smartctl -H "$disk" 2>/dev/null | grep "SMART overall-health" | awk '{print $6}' 2>/dev/null) || smart_status="Unknown" + info+="$disk: $smart_status\n" + fi + done + info+="\n" + fi + + # Mount Information + info+="MOUNT POINTS\n" + info+="─────────────────────────────────────\n" + local mount_info + mount_info=$(mount 2>/dev/null | grep -E '^/dev/' | awk '{print $1 " on " $3 " type " $5}' 2>/dev/null) + if [[ -n "$mount_info" ]]; then + info+="$mount_info\n\n" + else + info+="Mount information not available\n\n" + fi + + # I/O Statistics + if [[ -f /proc/diskstats ]]; then + info+="DISK I/O STATISTICS\n" + info+="─────────────────────────────────────\n" + local io_stats + io_stats=$(awk '$3 ~ /^(sd|nvme|hd)/ {printf "%-10s: %10s reads, %10s writes\n", $3, $4, $8}' /proc/diskstats 2>/dev/null | head -10) + if [[ -n "$io_stats" ]]; then + info+="$io_stats\n" + else + info+="I/O statistics not available\n" + fi + fi + + ui_info "Storage & Disk Details" "$info" + return 0 +} + +show_network_details() { + log "Starting show_network_details function" + + local info="NETWORK & CONNECTIVITY DETAILS\n" + info+="═══════════════════════════════════════\n\n" + + # Network Interfaces + info+="NETWORK INTERFACES\n" + info+="─────────────────────────────────────\n" + if command -v ip &>/dev/null; then + local interfaces + interfaces=$(ip -o link show 2>/dev/null | awk -F': ' '{print $2 " (" $3 ")"}' 2>/dev/null) + if [[ -n "$interfaces" ]]; then + info+="$interfaces\n\n" + else + info+="No interfaces detected\n\n" + fi + fi + + # IP Addresses + info+="IP ADDRESSES\n" + info+="─────────────────────────────────────\n" + if command -v ip &>/dev/null; then + local ip_addresses + ip_addresses=$(ip -o addr show 2>/dev/null | awk '{print $2 ": " $4}' | grep -v "127.0.0.1\|::1" 2>/dev/null) + if [[ -n "$ip_addresses" ]]; then + info+="$ip_addresses\n\n" + else + info+="No IP addresses configured\n\n" + fi + fi + + # Routing Information + info+="ROUTING TABLE\n" + info+="─────────────────────────────────────\n" + if command -v ip &>/dev/null; then + local routes + routes=$(ip route show 2>/dev/null | head -10 2>/dev/null) + if [[ -n "$routes" ]]; then + info+="$routes\n\n" + else + info+="No routes configured\n\n" + fi + fi + + # DNS Configuration + info+="DNS CONFIGURATION\n" + info+="─────────────────────────────────────\n" + if [[ -f /etc/resolv.conf ]]; then + local dns_servers + dns_servers=$(grep "^nameserver" /etc/resolv.conf 2>/dev/null | awk '{print "Nameserver: " $2}' 2>/dev/null) + if [[ -n "$dns_servers" ]]; then + info+="$dns_servers\n" + else + info+="No DNS servers configured\n" + fi + + local search_domains + search_domains=$(grep "^search" /etc/resolv.conf 2>/dev/null | cut -d' ' -f2- 2>/dev/null) + [[ -n "$search_domains" ]] && info+="Search domains: $search_domains\n" + info+="\n" + fi + + # Network Statistics + info+="NETWORK STATISTICS\n" + info+="─────────────────────────────────────\n" + if [[ -f /proc/net/dev ]]; then + local net_stats + net_stats=$(awk 'NR>2 && $1 !~ /lo:/ {gsub(/:/, "", $1); printf "%-10s: RX %10s bytes, TX %10s bytes\n", $1, $2, $10}' /proc/net/dev 2>/dev/null | head -10) + if [[ -n "$net_stats" ]]; then + info+="$net_stats\n\n" + else + info+="Network statistics not available\n\n" + fi + fi + + # Connectivity Test + info+="CONNECTIVITY TEST\n" + info+="─────────────────────────────────────\n" + local internet_status ping_time + if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then + internet_status="Connected" + ping_time=$(ping -c 1 8.8.8.8 2>/dev/null | grep "time=" | awk -F'time=' '{print $2}' | awk '{print $1}' 2>/dev/null) || ping_time="Unknown" + info+="Internet: $internet_status (ping: ${ping_time}ms)\n" + else + internet_status="Disconnected" + info+="Internet: $internet_status\n" + fi + + ui_info "Network & Connectivity Details" "$info" + return 0 +} + +show_graphics_details() { + log "Starting show_graphics_details function" + + local info="GRAPHICS & DISPLAY INFORMATION\n" + info+="═══════════════════════════════════════\n\n" + + # GPU Information + info+="GRAPHICS CARDS\n" + info+="─────────────────────────────────────\n" + local gpu_info="" + if command -v lspci &>/dev/null; then + gpu_info=$(lspci 2>/dev/null | grep -i "vga\|3d\|display" | sed 's/^[0-9a-f:.]* //' 2>/dev/null) + if [[ -n "$gpu_info" ]]; then + info+="$gpu_info\n\n" + else + info+="No PCI graphics devices detected\n\n" + fi + fi + + # Display Information + info+="CONNECTED DISPLAYS\n" + info+="─────────────────────────────────────\n" + local display_info="" + + # Try xrandr first (X11) + if command -v xrandr &>/dev/null && [[ -n "$DISPLAY" ]]; then + display_info=$(xrandr 2>/dev/null | grep " connected" | awk '{print $1 " - " $3 " " $4}' 2>/dev/null) + if [[ -n "$display_info" ]]; then + info+="$display_info\n\n" + fi + fi + + # Fallback to DRM + if [[ -z "$display_info" ]] && [[ -d /sys/class/drm ]]; then + for connector in /sys/class/drm/card*/card*-*/status; do + if [[ -f "$connector" ]]; then + local status connector_name + status=$(cat "$connector" 2>/dev/null) + connector_name=$(basename "$(dirname "$connector")" | sed 's/card[0-9]*-//') + + if [[ "$status" == "connected" ]]; then + local modes_file="$(dirname "$connector")/modes" + local resolution="Unknown" + if [[ -f "$modes_file" ]]; then + resolution=$(head -1 "$modes_file" 2>/dev/null) || resolution="Unknown" + fi + info+="$connector_name - $resolution\n" + fi + fi + done + info+="\n" + fi + + [[ -z "$display_info" ]] && info+="No connected displays detected\n\n" + + # OpenGL Information + if command -v glxinfo &>/dev/null && [[ -n "$DISPLAY" ]]; then + info+="OPENGL INFORMATION\n" + info+="─────────────────────────────────────\n" + local gl_vendor gl_renderer gl_version + gl_vendor=$(glxinfo 2>/dev/null | grep "OpenGL vendor" | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || gl_vendor="Unknown" + gl_renderer=$(glxinfo 2>/dev/null | grep "OpenGL renderer" | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || gl_renderer="Unknown" + gl_version=$(glxinfo 2>/dev/null | grep "OpenGL version" | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || gl_version="Unknown" + + info+="Vendor: $gl_vendor\n" + info+="Renderer: $gl_renderer\n" + info+="Version: $gl_version\n\n" + fi + + # Framebuffer Information + if [[ -d /sys/class/graphics ]]; then + info+="FRAMEBUFFER DEVICES\n" + info+="─────────────────────────────────────\n" + for fb in /sys/class/graphics/fb*/virtual_size; do + if [[ -f "$fb" ]]; then + local fb_name resolution + fb_name=$(basename "$(dirname "$fb")") + resolution=$(cat "$fb" 2>/dev/null | tr ',' 'x' 2>/dev/null) || resolution="Unknown" + info+="$fb_name: ${resolution}\n" + fi + done + info+="\n" + fi + + ui_info "Graphics & Display Information" "$info" + return 0 +} + +show_audio_details() { + log "Starting show_audio_details function" + + local info="AUDIO & SOUND DEVICES\n" + info+="═══════════════════════════════════════\n\n" + + # ALSA Sound Cards + if [[ -f /proc/asound/cards ]]; then + info+="ALSA SOUND CARDS\n" + info+="─────────────────────────────────────\n" + local alsa_cards + alsa_cards=$(cat /proc/asound/cards 2>/dev/null) + if [[ -n "$alsa_cards" ]]; then + info+="$alsa_cards\n\n" + else + info+="No ALSA sound cards detected\n\n" + fi + fi + + # PCI Audio Devices + if command -v lspci &>/dev/null; then + info+="PCI AUDIO CONTROLLERS\n" + info+="─────────────────────────────────────\n" + local pci_audio + pci_audio=$(lspci 2>/dev/null | grep -i "audio\|sound" | sed 's/^[0-9a-f:.]* //' 2>/dev/null) + if [[ -n "$pci_audio" ]]; then + info+="$pci_audio\n\n" + else + info+="No PCI audio devices detected\n\n" + fi + fi + + # Audio Devices + if [[ -d /proc/asound ]]; then + info+="AUDIO DEVICE DETAILS\n" + info+="─────────────────────────────────────\n" + for card in /proc/asound/card*/id; do + if [[ -f "$card" ]]; then + local card_id card_name + card_id=$(cat "$card" 2>/dev/null) || card_id="Unknown" + card_name=$(cat "$(dirname "$card")/id" 2>/dev/null) || card_name="Unknown" + info+="Card: $card_id\n" + + # Get PCM devices for this card + local pcm_devices + pcm_devices=$(find "$(dirname "$card")" -name "pcm*p" -o -name "pcm*c" 2>/dev/null | wc -l 2>/dev/null) || pcm_devices="0" + info+="PCM devices: $pcm_devices\n\n" + fi + done + fi + + # PulseAudio Information + if command -v pactl &>/dev/null; then + info+="PULSEAUDIO INFORMATION\n" + info+="─────────────────────────────────────\n" + local pa_sinks pa_sources + pa_sinks=$(pactl list short sinks 2>/dev/null | wc -l 2>/dev/null) || pa_sinks="0" + pa_sources=$(pactl list short sources 2>/dev/null | wc -l 2>/dev/null) || pa_sources="0" + + info+="Audio sinks: $pa_sinks\n" + info+="Audio sources: $pa_sources\n\n" + + # Default sink + local default_sink + default_sink=$(pactl get-default-sink 2>/dev/null) || default_sink="Unknown" + info+="Default sink: $default_sink\n\n" + fi + + ui_info "Audio & Sound Devices" "$info" + return 0 +} + +show_package_details() { + log "Starting show_package_details function" + + local info="PACKAGE MANAGERS & SOFTWARE\n" + info+="═══════════════════════════════════════\n\n" + + # APT Package Manager + info+="APT PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + local apt_installed apt_upgradable apt_auto_removable + apt_installed=$(dpkg -l 2>/dev/null | grep -c "^ii" 2>/dev/null) || apt_installed="0" + apt_upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable" 2>/dev/null) || apt_upgradable="0" + apt_auto_removable=$(apt autoremove --dry-run 2>/dev/null | grep -c "^Remv" 2>/dev/null) || apt_auto_removable="0" + + info+="Installed packages: $apt_installed\n" + info+="Upgradable packages: $apt_upgradable\n" + info+="Auto-removable packages: $apt_auto_removable\n\n" + + # Snap Package Manager + if command -v snap &>/dev/null; then + info+="SNAP PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + local snap_installed snap_refreshable + snap_installed=$(snap list 2>/dev/null | tail -n +2 | wc -l 2>/dev/null) || snap_installed="0" + snap_refreshable=$(snap refresh --list 2>/dev/null | tail -n +2 | wc -l 2>/dev/null) || snap_refreshable="0" + + info+="Installed snaps: $snap_installed\n" + info+="Refreshable snaps: $snap_refreshable\n\n" + else + info+="SNAP PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + info+="Snap not available\n\n" + fi + + # Flatpak Package Manager + if command -v flatpak &>/dev/null; then + info+="FLATPAK PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + local flatpak_apps flatpak_updates flatpak_remotes + flatpak_apps=$(flatpak list --app 2>/dev/null | wc -l 2>/dev/null) || flatpak_apps="0" + flatpak_updates=$(flatpak remote-ls --updates 2>/dev/null | wc -l 2>/dev/null) || flatpak_updates="0" + flatpak_remotes=$(flatpak remotes 2>/dev/null | tail -n +2 | wc -l 2>/dev/null) || flatpak_remotes="0" + + info+="Installed applications: $flatpak_apps\n" + info+="Available updates: $flatpak_updates\n" + info+="Configured remotes: $flatpak_remotes\n\n" + else + info+="FLATPAK PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + info+="Flatpak not available\n\n" + fi + + # Python Package Manager + if command -v pip3 &>/dev/null || command -v pip &>/dev/null; then + info+="PYTHON PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + local pip_packages + pip_packages=$(pip3 list 2>/dev/null | tail -n +3 | wc -l 2>/dev/null) || pip_packages="0" + [[ "$pip_packages" == "0" ]] && pip_packages=$(pip list 2>/dev/null | tail -n +3 | wc -l 2>/dev/null) || pip_packages="0" + + info+="Installed packages: $pip_packages\n\n" + else + info+="PYTHON PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + info+="Python pip not available\n\n" + fi + + # Node.js Package Manager + if command -v npm &>/dev/null; then + info+="NODE.JS PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + local npm_global npm_version + npm_global=$(npm list -g --depth=0 2>/dev/null | grep -c "├──\|└──" 2>/dev/null) || npm_global="0" + npm_version=$(npm --version 2>/dev/null) || npm_version="Unknown" + + info+="Global packages: $npm_global\n" + info+="NPM version: $npm_version\n\n" + else + info+="NODE.JS PACKAGE MANAGER\n" + info+="─────────────────────────────────────\n" + info+="Node.js npm not available\n\n" + fi + + # Ultrabunt Package Statistics + info+="ULTRABUNT PACKAGE STATISTICS\n" + info+="─────────────────────────────────────\n" + local total_pkgs=${#PACKAGES[@]} + local installed_count=0 + + for name in "${!PACKAGES[@]}"; do + if is_package_installed "$name" 2>/dev/null; then + ((installed_count++)) 2>/dev/null || true + fi + done 2>/dev/null || true + + info+="Tracked packages: $total_pkgs\n" + info+="Installed packages: $installed_count\n" + info+="Available packages: $((total_pkgs - installed_count))\n" + + ui_info "Package Managers & Software" "$info" + return 0 +} + +show_legacy_system_info() { + log "Starting show_legacy_system_info function" + + local info="COMPLETE SYSTEM INFORMATION\n" info+="═══════════════════════════════════════\n\n" # OS Info @@ -7351,7 +8745,6 @@ show_system_info() { local os_name="Unknown" local os_version="N/A" - # Safely parse os-release while IFS='=' read -r key value 2>/dev/null; do case "$key" in PRETTY_NAME) os_name="${value//\"/}" ;; @@ -7366,24 +8759,43 @@ show_system_info() { # Kernel local kernel_version kernel_version=$(uname -r 2>/dev/null) || kernel_version="Unknown" - info+="Kernel: $kernel_version\n\n" + info+="Kernel: $kernel_version\n" - # Hardware Info - info+="HARDWARE INFORMATION\n" - info+="─────────────────────────────────────\n" + # System Model + local system_model="Unknown" + local system_vendor="Unknown" - # CPU Info - local cpu_info - if [[ -f /proc/cpuinfo ]]; then - cpu_info=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_info="Unknown" - local cpu_cores - cpu_cores=$(nproc 2>/dev/null) || cpu_cores="Unknown" - info+="CPU: $cpu_info ($cpu_cores cores)\n" - else - info+="CPU: Information not available\n" + if [[ -f /sys/class/dmi/id/product_name ]]; then + system_model=$(cat /sys/class/dmi/id/product_name 2>/dev/null | tr -d '\0' 2>/dev/null) || system_model="Unknown" fi - # Memory Info + if [[ -f /sys/class/dmi/id/sys_vendor ]]; then + system_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null | tr -d '\0' 2>/dev/null) || system_vendor="Unknown" + fi + + if [[ "$system_vendor" != "Unknown" && "$system_model" != "Unknown" ]]; then + info+="System: $system_vendor $system_model\n" + elif [[ "$system_model" != "Unknown" ]]; then + info+="System: $system_model\n" + else + info+="System: Model information not available\n" + fi + + info+="\n" + + # Hardware Summary + info+="HARDWARE SUMMARY\n" + info+="─────────────────────────────────────\n" + + # CPU + if [[ -f /proc/cpuinfo ]]; then + local cpu_info cpu_cores + cpu_info=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_info="Unknown" + cpu_cores=$(nproc 2>/dev/null) || cpu_cores="Unknown" + info+="CPU: $cpu_info ($cpu_cores cores)\n" + fi + + # Memory if [[ -f /proc/meminfo ]]; then local mem_total mem_available mem_total=$(grep "MemTotal:" /proc/meminfo 2>/dev/null | awk '{print int($2/1024)}' 2>/dev/null) || mem_total="Unknown" @@ -7391,151 +8803,42 @@ show_system_info() { if [[ "$mem_total" != "Unknown" && "$mem_available" != "Unknown" ]]; then local mem_used=$((mem_total - mem_available)) info+="Memory: ${mem_used}MB used / ${mem_total}MB total\n" - else - info+="Memory: Information not available\n" fi - else - info+="Memory: Information not available\n" fi - # Storage Information - info+="STORAGE INFORMATION\n" - info+="─────────────────────────────────────\n" - - # Root filesystem + # Storage local disk_info - disk_info=$(df -h / 2>/dev/null | tail -1 2>/dev/null | awk '{print $3 " used / " $2 " total (" $5 " full)"}' 2>/dev/null) || disk_info="Information not available" - info+="Root (/): $disk_info\n" + disk_info=$(df -h / 2>/dev/null | tail -1 | awk '{print $3 " used / " $2 " total (" $5 " full)"}' 2>/dev/null) || disk_info="Information not available" + info+="Storage: $disk_info\n" - # All mounted filesystems (excluding special filesystems) - local mount_info - mount_info=$(df -h 2>/dev/null | grep -E '^/dev/' | grep -v -E '(tmpfs|udev|overlay)' | awk '{print $6 ": " $3 " used / " $2 " total (" $5 " full)"}' 2>/dev/null | tail -n +2 2>/dev/null) - if [[ -n "$mount_info" ]]; then - while IFS= read -r line; do - [[ "$line" != *"/ :"* ]] && info+="$line\n" - done <<< "$mount_info" - fi - - # Swap information - local swap_info - swap_info=$(free -h 2>/dev/null | grep "Swap:" 2>/dev/null | awk '{print $3 " used / " $2 " total"}' 2>/dev/null) || swap_info="Not available" - info+="Swap: $swap_info\n" - - # Disk usage summary - local total_disk_count - total_disk_count=$(lsblk -d 2>/dev/null | grep -c "disk" 2>/dev/null) || total_disk_count="Unknown" - info+="Physical disks: $total_disk_count detected\n\n" - - # System Uptime and Load - local uptime_info load_avg - uptime_info=$(uptime -p 2>/dev/null | sed 's/up //' 2>/dev/null) || uptime_info="Unknown" - load_avg=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' 2>/dev/null | sed 's/^ *//' 2>/dev/null) || load_avg="Unknown" - - info+="Uptime: $uptime_info\n" - info+="Load average:$load_avg\n" - - # Process count - local process_count - process_count=$(ps aux 2>/dev/null | wc -l 2>/dev/null) || process_count="Unknown" - [[ "$process_count" != "Unknown" ]] && process_count=$((process_count - 1)) # Subtract header line - info+="Running processes: $process_count\n\n" - - # Buntage Managers - info+="BUNTAGE MANAGERS\n" - info+="─────────────────────────────────────\n" - - # APT detailed info - local apt_count apt_upgradable apt_auto_removable - apt_count=$(dpkg -l 2>/dev/null | grep -c "^ii" 2>/dev/null) || apt_count="0" - apt_upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable" 2>/dev/null) || apt_upgradable="0" - apt_auto_removable=$(apt autoremove --dry-run 2>/dev/null | grep -c "^Remv" 2>/dev/null) || apt_auto_removable="0" - - info+="APT: $apt_count installed" - [[ "$apt_upgradable" != "0" ]] && info+=" ($apt_upgradable upgradable)" - [[ "$apt_auto_removable" != "0" ]] && info+=" ($apt_auto_removable auto-removable)" - info+="\n" - - # Snap detailed info - if command -v snap &>/dev/null; then - local snap_count snap_refreshable - snap_count=$(snap list 2>/dev/null | tail -n +2 2>/dev/null | wc -l 2>/dev/null) || snap_count="0" - snap_refreshable=$(snap refresh --list 2>/dev/null | tail -n +2 2>/dev/null | wc -l 2>/dev/null) || snap_refreshable="0" - - info+="Snap: $snap_count installed" - [[ "$snap_refreshable" != "0" ]] && info+=" ($snap_refreshable refreshable)" - info+="\n" - else - info+="Snap: Not available\n" - fi - - # Flatpak detailed info - if command -v flatpak &>/dev/null; then - local flat_count flat_updates flat_remotes - flat_count=$(flatpak list --app 2>/dev/null | wc -l 2>/dev/null) || flat_count="0" - flat_updates=$(flatpak remote-ls --updates 2>/dev/null | wc -l 2>/dev/null) || flat_updates="0" - flat_remotes=$(flatpak remotes 2>/dev/null | tail -n +2 2>/dev/null | wc -l 2>/dev/null) || flat_remotes="0" - - info+="Flatpak: $flat_count apps" - [[ "$flat_updates" != "0" ]] && info+=" ($flat_updates updates)" - info+=" [$flat_remotes remotes]\n" - else - info+="Flatpak: Not available\n" - fi - - # Additional buntage managers - if command -v pip3 &>/dev/null || command -v pip &>/dev/null; then - local pip_count - pip_count=$(pip3 list 2>/dev/null | tail -n +3 2>/dev/null | wc -l 2>/dev/null) || pip_count="0" - [[ "$pip_count" == "0" ]] && pip_count=$(pip list 2>/dev/null | tail -n +3 2>/dev/null | wc -l 2>/dev/null) || pip_count="0" - info+="Python (pip): $pip_count packages\n" - fi - - if command -v npm &>/dev/null; then - local npm_global_count - npm_global_count=$(npm list -g --depth=0 2>/dev/null | grep -c "├──\|└──" 2>/dev/null) || npm_global_count="0" - info+="Node.js (npm global): $npm_global_count packages\n" - fi - - info+="\n" - - # Network Information - info+="NETWORK INFORMATION\n" - info+="─────────────────────────────────────\n" - - # Active network interfaces - local active_interfaces - active_interfaces=$(ip -o link show 2>/dev/null | awk -F': ' '$3 !~ /lo|docker|br-/ && $3 ~ /UP/ {print $2}' 2>/dev/null | tr '\n' ' ' 2>/dev/null) || active_interfaces="Unknown" - [[ -n "$active_interfaces" ]] && info+="Active interfaces: $active_interfaces\n" || info+="Active interfaces: None detected\n" - - # IP addresses + # Network local primary_ip primary_ip=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $7; exit}' 2>/dev/null) || primary_ip="Unknown" info+="Primary IP: $primary_ip\n" - # Internet connectivity check - local internet_status - if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then - internet_status="Connected" - else - internet_status="Disconnected" - fi - info+="Internet: $internet_status\n" + # Uptime + local uptime_info + uptime_info=$(uptime -p 2>/dev/null | sed 's/up //' 2>/dev/null) || uptime_info="Unknown" + info+="Uptime: $uptime_info\n\n" - # DNS servers - local dns_servers - dns_servers=$(grep "^nameserver" /etc/resolv.conf 2>/dev/null | awk '{print $2}' 2>/dev/null | tr '\n' ' ' 2>/dev/null) || dns_servers="Unknown" - [[ -n "$dns_servers" ]] && info+="DNS servers: $dns_servers\n" || info+="DNS servers: Not configured\n" + # Package Summary + info+="PACKAGE SUMMARY\n" + info+="─────────────────────────────────────\n" + local apt_count snap_count flatpak_count + apt_count=$(dpkg -l 2>/dev/null | grep -c "^ii" 2>/dev/null) || apt_count="0" + snap_count=$(snap list 2>/dev/null | tail -n +2 | wc -l 2>/dev/null) || snap_count="0" + flatpak_count=$(flatpak list --app 2>/dev/null | wc -l 2>/dev/null) || flatpak_count="0" - info+="\n" + info+="APT packages: $apt_count\n" + info+="Snap packages: $snap_count\n" + info+="Flatpak apps: $flatpak_count\n\n" # Ultrabunt Stats info+="ULTRABUNT STATISTICS\n" info+="─────────────────────────────────────\n" - local total_pkgs=${#PACKAGES[@]} local installed_count=0 - # Safely count installed packages for name in "${!PACKAGES[@]}"; do if is_package_installed "$name" 2>/dev/null; then ((installed_count++)) 2>/dev/null || true @@ -7544,18 +8847,645 @@ show_system_info() { info+="Tracked packages: $total_pkgs\n" info+="Installed: $installed_count\n" - info+="Available: $((total_pkgs - installed_count))\n\n" + info+="Available: $((total_pkgs - installed_count))\n" - info+="Log file: $LOGFILE\n" - info+="Backup dir: $BACKUP_DIR\n" + ui_info "Complete System Information" "$info" + return 0 +} + +show_hardware_details() { + log "Starting show_hardware_details function" - log "Calling ui_info with system information" - ui_info "System Information" "$info" || { - log "ERROR: ui_info failed in show_system_info" - return 1 - } + local info="HARDWARE & SYSTEM INFORMATION\n" + info+="═══════════════════════════════════════\n\n" - log "show_system_info completed successfully" + # System Information + info+="SYSTEM INFORMATION\n" + info+="─────────────────────────────────────\n" + + local system_vendor system_model system_version system_serial bios_vendor bios_version bios_date + + # Try DMI first + if command -v dmidecode &>/dev/null; then + system_vendor=$(dmidecode -s system-manufacturer 2>/dev/null | head -1 2>/dev/null) || system_vendor="Unknown" + system_model=$(dmidecode -s system-product-name 2>/dev/null | head -1 2>/dev/null) || system_model="Unknown" + system_version=$(dmidecode -s system-version 2>/dev/null | head -1 2>/dev/null) || system_version="Unknown" + system_serial=$(dmidecode -s system-serial-number 2>/dev/null | head -1 2>/dev/null) || system_serial="Unknown" + bios_vendor=$(dmidecode -s bios-vendor 2>/dev/null | head -1 2>/dev/null) || bios_vendor="Unknown" + bios_version=$(dmidecode -s bios-version 2>/dev/null | head -1 2>/dev/null) || bios_version="Unknown" + bios_date=$(dmidecode -s bios-release-date 2>/dev/null | head -1 2>/dev/null) || bios_date="Unknown" + fi + + # Fallback to /sys/class/dmi/id/ + [[ "$system_vendor" == "Unknown" ]] && [[ -f /sys/class/dmi/id/sys_vendor ]] && system_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null | tr -d '\0' 2>/dev/null) || system_vendor="Unknown" + [[ "$system_model" == "Unknown" ]] && [[ -f /sys/class/dmi/id/product_name ]] && system_model=$(cat /sys/class/dmi/id/product_name 2>/dev/null | tr -d '\0' 2>/dev/null) || system_model="Unknown" + [[ "$bios_vendor" == "Unknown" ]] && [[ -f /sys/class/dmi/id/bios_vendor ]] && bios_vendor=$(cat /sys/class/dmi/id/bios_vendor 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_vendor="Unknown" + [[ "$bios_version" == "Unknown" ]] && [[ -f /sys/class/dmi/id/bios_version ]] && bios_version=$(cat /sys/class/dmi/id/bios_version 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_version="Unknown" + [[ "$bios_date" == "Unknown" ]] && [[ -f /sys/class/dmi/id/bios_date ]] && bios_date=$(cat /sys/class/dmi/id/bios_date 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_date="Unknown" + + + + info+="Manufacturer: $system_vendor\n" + info+="Model: $system_model\n" + info+="Version: $system_version\n" + info+="Serial Number: $system_serial\n\n" + + info+="BIOS/UEFI INFORMATION\n" + info+="─────────────────────────────────────\n" + info+="Vendor: $bios_vendor\n" + info+="Version: $bios_version\n" + info+="Release Date: $bios_date\n\n" + + # Motherboard Information + if command -v dmidecode &>/dev/null; then + info+="MOTHERBOARD INFORMATION\n" + info+="─────────────────────────────────────\n" + local mb_manufacturer mb_product mb_version mb_serial + mb_manufacturer=$(dmidecode -s baseboard-manufacturer 2>/dev/null | head -1 2>/dev/null) || mb_manufacturer="Unknown" + mb_product=$(dmidecode -s baseboard-product-name 2>/dev/null | head -1 2>/dev/null) || mb_product="Unknown" + mb_version=$(dmidecode -s baseboard-version 2>/dev/null | head -1 2>/dev/null) || mb_version="Unknown" + mb_serial=$(dmidecode -s baseboard-serial-number 2>/dev/null | head -1 2>/dev/null) || mb_serial="Unknown" + + info+="Manufacturer: $mb_manufacturer\n" + info+="Product: $mb_product\n" + info+="Version: $mb_version\n" + info+="Serial: $mb_serial\n\n" + fi + + # PCI Devices + if command -v lspci &>/dev/null; then + info+="PCI DEVICES\n" + info+="─────────────────────────────────────\n" + local pci_devices + pci_devices=$(lspci 2>/dev/null | head -15 | sed 's/^[0-9a-f:.]* //' 2>/dev/null) + if [[ -n "$pci_devices" ]]; then + info+="$pci_devices\n" + local pci_count + pci_count=$(lspci 2>/dev/null | wc -l 2>/dev/null) || pci_count="0" + [[ "$pci_count" -gt 15 ]] && info+="... and $((pci_count - 15)) more devices\n" + else + info+="PCI device information not available\n" + fi + info+="\n" + fi + + # System Uptime and Load + info+="SYSTEM STATUS\n" + info+="─────────────────────────────────────\n" + local uptime_info load_avg boot_time + uptime_info=$(uptime -p 2>/dev/null | sed 's/up //' 2>/dev/null) || uptime_info="Unknown" + load_avg=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' 2>/dev/null | sed 's/^ *//' 2>/dev/null) || load_avg="Unknown" + boot_time=$(who -b 2>/dev/null | awk '{print $3 " " $4}' 2>/dev/null) || boot_time="Unknown" + + info+="Uptime: $uptime_info\n" + info+="Load Average:$load_avg\n" + info+="Boot Time: $boot_time\n\n" + + # Process Information + local process_count zombie_count + process_count=$(ps aux 2>/dev/null | wc -l 2>/dev/null) || process_count="Unknown" + [[ "$process_count" != "Unknown" ]] && process_count=$((process_count - 1)) + zombie_count=$(ps aux 2>/dev/null | awk '$8 ~ /^Z/ {count++} END {print count+0}' 2>/dev/null) || zombie_count="0" + + info+="Running Processes: $process_count\n" + info+="Zombie Processes: $zombie_count\n" + + ui_info "Hardware & System Information" "$info" + return 0 +} + +show_system_info() { + log "Starting enhanced show_system_info function" + + while true; do + local menu_items=() + + # Check if running as root/sudo for enhanced information + local sudo_warning="" + if [[ $EUID -ne 0 ]]; then + sudo_warning="⚠️ Run this script as superuser (sudo) to see the magic inside!\n\n" + fi + + # Get key system info for overview + local os_name="Unknown" + local os_version="N/A" + if [[ -f /etc/os-release ]]; then + while IFS='=' read -r key value 2>/dev/null; do + case "$key" in + PRETTY_NAME) os_name="${value//\"/}" ;; + VERSION) os_version="${value//\"/}" ;; + esac + done < /etc/os-release 2>/dev/null || true + fi + + local kernel_version + kernel_version=$(uname -r 2>/dev/null) || kernel_version="Unknown" + + # Enhanced CPU information extraction + local cpu_model cpu_cores cpu_threads cpu_freq cpu_max_freq cpu_family + if [[ -f /proc/cpuinfo ]]; then + cpu_model=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_model="Unknown" + cpu_cores=$(grep "cpu cores" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_cores="Unknown" + cpu_threads=$(grep -c "^processor" /proc/cpuinfo 2>/dev/null) || cpu_threads="Unknown" + cpu_freq=$(grep "cpu MHz" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_freq="Unknown" + cpu_family=$(grep "cpu family" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_family="Unknown" + + # Fallback for cores if "cpu cores" not found + if [[ "$cpu_cores" == "Unknown" ]]; then + cpu_cores=$(grep "siblings" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_cores="$cpu_threads" + fi + + # Get max frequency from lscpu if available + if command -v lscpu &>/dev/null; then + cpu_max_freq=$(lscpu 2>/dev/null | grep "CPU max MHz" | awk '{print $4}' 2>/dev/null) || cpu_max_freq="Unknown" + [[ "$cpu_max_freq" != "Unknown" ]] && cpu_max_freq="${cpu_max_freq} MHz" + + # Also try to get model from lscpu if /proc/cpuinfo failed + if [[ "$cpu_model" == "Unknown" ]]; then + cpu_model=$(lscpu 2>/dev/null | grep "Model name:" | cut -d: -f2 | sed 's/^ *//' 2>/dev/null) || cpu_model="Unknown" + fi + else + cpu_max_freq="Unknown" + fi + + [[ "$cpu_freq" != "Unknown" ]] && cpu_freq="${cpu_freq} MHz" + else + cpu_model="Unknown" + cpu_cores="Unknown" + cpu_threads="Unknown" + cpu_freq="Unknown" + cpu_max_freq="Unknown" + cpu_family="Unknown" + fi + + # System model and serial information with enhanced detection + local system_model system_serial system_manufacturer system_family system_version + if command -v dmidecode &>/dev/null && [[ $EUID -eq 0 ]]; then + system_manufacturer=$(dmidecode -s system-manufacturer 2>/dev/null | head -1 2>/dev/null) || system_manufacturer="Unknown" + system_model=$(dmidecode -s system-product-name 2>/dev/null | head -1 2>/dev/null) || system_model="Unknown" + system_serial=$(dmidecode -s system-serial-number 2>/dev/null | head -1 2>/dev/null) || system_serial="Unknown" + system_family=$(dmidecode -s system-family 2>/dev/null | head -1 2>/dev/null) || system_family="Unknown" + system_version=$(dmidecode -s system-version 2>/dev/null | head -1 2>/dev/null) || system_version="Unknown" + else + # Fallback methods for non-root users + system_manufacturer=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) || system_manufacturer="Unknown" + system_model=$(cat /sys/class/dmi/id/product_name 2>/dev/null) || system_model="Unknown" + system_serial=$(cat /sys/class/dmi/id/product_serial 2>/dev/null) || system_serial="Unknown" + system_family=$(cat /sys/class/dmi/id/product_family 2>/dev/null) || system_family="Unknown" + system_version=$(cat /sys/class/dmi/id/product_version 2>/dev/null) || system_version="Unknown" + fi + + # Memory information with detailed specs - convert to GB for consistency + local memory_total memory_used memory_available memory_speed memory_type memory_slots + if command -v free &>/dev/null; then + # Get memory in bytes and convert to GB for cleaner display + local mem_total_bytes mem_used_bytes mem_available_bytes + mem_total_bytes=$(free -b 2>/dev/null | grep "Mem:" | awk '{print $2}' 2>/dev/null) || mem_total_bytes="0" + mem_used_bytes=$(free -b 2>/dev/null | grep "Mem:" | awk '{print $3}' 2>/dev/null) || mem_used_bytes="0" + mem_available_bytes=$(free -b 2>/dev/null | grep "Mem:" | awk '{print $7}' 2>/dev/null) || mem_available_bytes="0" + + # Convert bytes to GB (decimal) for cleaner display + if [[ "$mem_total_bytes" -gt 0 ]]; then + memory_total=$(awk "BEGIN {printf \"%.1fGB\", $mem_total_bytes/1000000000}") + else + memory_total="Unknown" + fi + + if [[ "$mem_used_bytes" -gt 0 ]]; then + memory_used=$(awk "BEGIN {printf \"%.1fGB\", $mem_used_bytes/1000000000}") + else + memory_used="Unknown" + fi + + if [[ "$mem_available_bytes" -gt 0 ]]; then + memory_available=$(awk "BEGIN {printf \"%.1fGB\", $mem_available_bytes/1000000000}") + else + memory_available="Unknown" + fi + else + memory_total="Unknown" + memory_used="Unknown" + memory_available="Unknown" + fi + + if command -v dmidecode &>/dev/null && [[ $EUID -eq 0 ]]; then + # Get memory type - try multiple approaches + memory_type=$(dmidecode -t memory 2>/dev/null | grep -E "^\s*Type:" | grep -v "Unknown" | grep -v "Error Correction Type" | head -1 | awk '{print $2}' 2>/dev/null) || memory_type="Unknown" + if [[ "$memory_type" == "Unknown" ]]; then + memory_type=$(dmidecode -t 17 2>/dev/null | grep -E "^\s*Type:" | grep -v "Unknown" | head -1 | awk '{print $2}' 2>/dev/null) || memory_type="Unknown" + fi + + # Get memory speed - try multiple approaches + memory_speed=$(dmidecode -t memory 2>/dev/null | grep -E "^\s*Speed:" | grep -v "Unknown" | head -1 | awk '{print $2, $3}' 2>/dev/null) || memory_speed="Unknown" + if [[ "$memory_speed" == "Unknown" ]]; then + memory_speed=$(dmidecode -t 17 2>/dev/null | grep -E "^\s*Speed:" | grep -v "Unknown" | head -1 | awk '{print $2, $3}' 2>/dev/null) || memory_speed="Unknown" + fi + + # Count memory slots with actual memory + memory_slots=$(dmidecode -t memory 2>/dev/null | grep -E "^\s*Size:" | grep -v "No Module Installed" | wc -l 2>/dev/null) || memory_slots="Unknown" + if [[ "$memory_slots" == "0" || "$memory_slots" == "Unknown" ]]; then + memory_slots=$(dmidecode -t 17 2>/dev/null | grep -E "^\s*Size:" | grep -v "No Module Installed" | wc -l 2>/dev/null) || memory_slots="Unknown" + fi + else + memory_type="Unknown" + memory_speed="Unknown" + memory_slots="Unknown" + fi + + # Storage information with usage percentage + local storage_total storage_used storage_available storage_usage_percent storage_model storage_type + if command -v df &>/dev/null; then + local df_output + df_output=$(df -h / 2>/dev/null | tail -1) + storage_total=$(echo "$df_output" | awk '{print $2}' 2>/dev/null) || storage_total="Unknown" + storage_used=$(echo "$df_output" | awk '{print $3}' 2>/dev/null) || storage_used="Unknown" + storage_available=$(echo "$df_output" | awk '{print $4}' 2>/dev/null) || storage_available="Unknown" + storage_usage_percent=$(echo "$df_output" | awk '{print $5}' 2>/dev/null) || storage_usage_percent="Unknown" + else + storage_total="Unknown" + storage_used="Unknown" + storage_available="Unknown" + storage_usage_percent="Unknown" + fi + + # Enhanced storage model detection with multiple devices + local storage_devices="" + if command -v lsblk &>/dev/null; then + # Get all block devices with size and model info + local block_devices=$(lsblk -dno NAME,SIZE,MODEL 2>/dev/null | grep -E "^(sd|hd|nvme|mmcblk)" 2>/dev/null) + if [[ -n "$block_devices" ]]; then + while IFS= read -r line; do + local device_name=$(echo "$line" | awk '{print $1}') + local device_size=$(echo "$line" | awk '{print $2}') + local device_model=$(echo "$line" | awk '{for(i=3;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/[[:space:]]*$//') + + if [[ -n "$device_name" && -n "$device_size" ]]; then + if [[ -z "$device_model" || "$device_model" == " " ]]; then + device_model="Unknown Model" + fi + if [[ -n "$storage_devices" ]]; then + storage_devices="$storage_devices, " + fi + storage_devices="$storage_devices$device_name ($device_size, $device_model)" + fi + done <<< "$block_devices" + fi + fi + + # Fallback to single device detection if no devices found + if [[ -z "$storage_devices" ]]; then + if [[ -f /sys/block/sda/device/model ]]; then + storage_model=$(cat /sys/block/sda/device/model 2>/dev/null | tr -d ' \t\n\r' 2>/dev/null) || storage_model="Unknown" + elif command -v lsblk &>/dev/null; then + storage_model=$(lsblk -d -o MODEL 2>/dev/null | grep -v "MODEL" | head -1 | tr -d ' \t\n\r' 2>/dev/null) || storage_model="Unknown" + else + storage_model="Unknown" + fi + storage_devices="Primary: $storage_model" + fi + + # Detect storage type (SSD/HDD) + if [[ -f /sys/block/sda/queue/rotational ]]; then + local rotational + rotational=$(cat /sys/block/sda/queue/rotational 2>/dev/null) + if [[ "$rotational" == "0" ]]; then + storage_type="SSD" + else + storage_type="HDD" + fi + else + storage_type="Unknown" + fi + + # GPU information + local gpu_info + if command -v lspci &>/dev/null; then + gpu_info=$(lspci 2>/dev/null | grep -i "vga\|3d\|display" | sed 's/^[0-9a-f:.]* //' | head -1 2>/dev/null) || gpu_info="Unknown" + else + gpu_info="Unknown" + fi + + # Enhanced port detection with proper formatting - avoid double counting + local usb_ports_list display_ports_list audio_ports_list thunderbolt_count + usb_ports_list="" + display_ports_list="" + audio_ports_list="" + thunderbolt_count="0" + + if command -v dmidecode &>/dev/null && [[ $EUID -eq 0 ]]; then + # Get unique port connectors from dmidecode to avoid duplicates + local port_connectors + port_connectors=$(dmidecode -t 8 2>/dev/null | grep -E "External Reference Designator|Port Type" | paste - - 2>/dev/null) + + # Count USB ports more accurately + local usb2_count usb3_count usbc_count + usb2_count=$(echo "$port_connectors" | grep -i "USB.*2\|USB" | grep -v "USB.*3\|USB-C" | wc -l 2>/dev/null) || usb2_count="0" + usb3_count=$(echo "$port_connectors" | grep -i "USB.*3" | wc -l 2>/dev/null) || usb3_count="0" + usbc_count=$(echo "$port_connectors" | grep -i "USB-C\|Type-C" | wc -l 2>/dev/null) || usbc_count="0" + + # If dmidecode shows no USB ports, use lsusb as fallback but be conservative + if [[ "$usb2_count" -eq 0 && "$usb3_count" -eq 0 && "$usbc_count" -eq 0 ]]; then + if command -v lsusb &>/dev/null; then + # Count USB controllers and estimate conservatively + local usb_controllers + usb_controllers=$(lspci 2>/dev/null | grep -i "usb controller" | wc -l 2>/dev/null) || usb_controllers="0" + if [[ "$usb_controllers" -gt 0 ]]; then + # Conservative estimate: 1-2 ports per controller + usb3_count="$usb_controllers" + fi + fi + fi + + # Format USB ports cleanly + local usb_parts=() + [[ "$usb2_count" -gt 0 ]] && usb_parts+=("${usb2_count} x USB 2.0") + [[ "$usb3_count" -gt 0 ]] && usb_parts+=("${usb3_count} x USB 3.0") + [[ "$usbc_count" -gt 0 ]] && usb_parts+=("${usbc_count} x USB-C") + + if [[ ${#usb_parts[@]} -gt 0 ]]; then + usb_ports_list=$(IFS=', '; echo "${usb_parts[*]}") + else + usb_ports_list="No USB ports detected" + fi + + # Count display ports more accurately + local hdmi_count mini_dp_count dp_count vga_count + hdmi_count=$(echo "$port_connectors" | grep -i "HDMI" | wc -l 2>/dev/null) || hdmi_count="0" + mini_dp_count=$(echo "$port_connectors" | grep -i "Mini.*Display\|Mini.*DP" | wc -l 2>/dev/null) || mini_dp_count="0" + dp_count=$(echo "$port_connectors" | grep -i "DisplayPort\|Display Port" | grep -v -i "Mini" | wc -l 2>/dev/null) || dp_count="0" + vga_count=$(echo "$port_connectors" | grep -i "VGA\|D-Sub" | wc -l 2>/dev/null) || vga_count="0" + + # Format display ports + local display_parts=() + [[ "$hdmi_count" -gt 0 ]] && display_parts+=("${hdmi_count} x HDMI") + [[ "$mini_dp_count" -gt 0 ]] && display_parts+=("${mini_dp_count} x Mini DisplayPort") + [[ "$dp_count" -gt 0 ]] && display_parts+=("${dp_count} x DisplayPort") + [[ "$vga_count" -gt 0 ]] && display_parts+=("${vga_count} x VGA") + + if [[ ${#display_parts[@]} -gt 0 ]]; then + display_ports_list=$(IFS=', '; echo "${display_parts[*]}") + else + display_ports_list="No display ports detected" + fi + + # Count Thunderbolt ports accurately + thunderbolt_count=$(echo "$port_connectors" | grep -i "Thunderbolt" | wc -l 2>/dev/null) || thunderbolt_count="0" + + # Count audio ports more accurately + local audio_count + audio_count=$(echo "$port_connectors" | grep -i "Audio\|Headphone\|Microphone" | wc -l 2>/dev/null) || audio_count="0" + [[ "$audio_count" -gt 0 ]] && audio_ports_list="${audio_count} x Audio" + else + # Simplified fallback for non-root users + usb_ports_list="Port detection requires sudo" + display_ports_list="Port detection requires sudo" + audio_ports_list="Port detection requires sudo" + fi + + # Network adapter information with connectivity + local network_adapters internet_status wifi_adapter eth_adapter + network_adapters="" + internet_status="❌ No connection" + wifi_adapter="Not detected" + eth_adapter="Not detected" + + # Check internet connectivity + if ping -c 1 8.8.8.8 &>/dev/null || ping -c 1 1.1.1.1 &>/dev/null; then + internet_status="✅ Connected" + fi + + # Detect network adapters + if command -v ip &>/dev/null; then + # Check for Wi-Fi adapter + if ip link show 2>/dev/null | grep -q "wl"; then + local wifi_interface + wifi_interface=$(ip link show 2>/dev/null | grep "wl" | head -1 | awk -F: '{print $2}' | tr -d ' ' 2>/dev/null) + if [[ -n "$wifi_interface" ]]; then + wifi_adapter="$wifi_interface (Wi-Fi)" + # Check if Wi-Fi is connected + if ip addr show "$wifi_interface" 2>/dev/null | grep -q "inet "; then + wifi_adapter="$wifi_adapter - Connected" + else + wifi_adapter="$wifi_adapter - Disconnected" + fi + fi + fi + + # Check for Ethernet adapter + if ip link show 2>/dev/null | grep -q "en\|eth"; then + local eth_interface + eth_interface=$(ip link show 2>/dev/null | grep -E "en|eth" | head -1 | awk -F: '{print $2}' | tr -d ' ' 2>/dev/null) + if [[ -n "$eth_interface" ]]; then + eth_adapter="$eth_interface (Ethernet)" + # Check if Ethernet is connected + if ip addr show "$eth_interface" 2>/dev/null | grep -q "inet "; then + eth_adapter="$eth_adapter - Connected" + else + eth_adapter="$eth_adapter - Disconnected" + fi + fi + fi + fi + + # Audio driver information + local audio_drivers audio_devices + audio_drivers="Unknown" + audio_devices="Unknown" + + if command -v lspci &>/dev/null; then + audio_drivers=$(lspci 2>/dev/null | grep -i "audio" | sed 's/^[0-9a-f:.]* //' | head -1 2>/dev/null) || audio_drivers="Unknown" + fi + + if [[ -d /proc/asound ]]; then + audio_devices=$(cat /proc/asound/cards 2>/dev/null | grep -E "^\s*[0-9]" | wc -l 2>/dev/null) || audio_devices="0" + [[ "$audio_devices" -gt 0 ]] && audio_devices="${audio_devices} audio device(s)" + fi + + local uptime_info + uptime_info=$(uptime -p 2>/dev/null | sed 's/up //' 2>/dev/null) || uptime_info="Unknown" + + # Get additional comprehensive system information + local bios_vendor bios_version bios_date mb_manufacturer mb_product mb_version mb_serial + local apt_count snap_count flatpak_count process_count zombie_count boot_time load_avg + + # BIOS/UEFI Information + if command -v dmidecode &>/dev/null && [[ $EUID -eq 0 ]]; then + bios_vendor=$(dmidecode -s bios-vendor 2>/dev/null | head -1 2>/dev/null) || bios_vendor="Unknown" + bios_version=$(dmidecode -s bios-version 2>/dev/null | head -1 2>/dev/null) || bios_version="Unknown" + bios_date=$(dmidecode -s bios-release-date 2>/dev/null | head -1 2>/dev/null) || bios_date="Unknown" + mb_manufacturer=$(dmidecode -s baseboard-manufacturer 2>/dev/null | head -1 2>/dev/null) || mb_manufacturer="Unknown" + mb_product=$(dmidecode -s baseboard-product-name 2>/dev/null | head -1 2>/dev/null) || mb_product="Unknown" + mb_version=$(dmidecode -s baseboard-version 2>/dev/null | head -1 2>/dev/null) || mb_version="Unknown" + mb_serial=$(dmidecode -s baseboard-serial-number 2>/dev/null | head -1 2>/dev/null) || mb_serial="Unknown" + else + # Fallback to /sys/class/dmi/id/ + [[ -f /sys/class/dmi/id/bios_vendor ]] && bios_vendor=$(cat /sys/class/dmi/id/bios_vendor 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_vendor="Unknown" + [[ -f /sys/class/dmi/id/bios_version ]] && bios_version=$(cat /sys/class/dmi/id/bios_version 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_version="Unknown" + [[ -f /sys/class/dmi/id/bios_date ]] && bios_date=$(cat /sys/class/dmi/id/bios_date 2>/dev/null | tr -d '\0' 2>/dev/null) || bios_date="Unknown" + [[ -f /sys/class/dmi/id/board_vendor ]] && mb_manufacturer=$(cat /sys/class/dmi/id/board_vendor 2>/dev/null | tr -d '\0' 2>/dev/null) || mb_manufacturer="Unknown" + [[ -f /sys/class/dmi/id/board_name ]] && mb_product=$(cat /sys/class/dmi/id/board_name 2>/dev/null | tr -d '\0' 2>/dev/null) || mb_product="Unknown" + [[ -f /sys/class/dmi/id/board_version ]] && mb_version=$(cat /sys/class/dmi/id/board_version 2>/dev/null | tr -d '\0' 2>/dev/null) || mb_version="Unknown" + [[ -f /sys/class/dmi/id/board_serial ]] && mb_serial=$(cat /sys/class/dmi/id/board_serial 2>/dev/null | tr -d '\0' 2>/dev/null) || mb_serial="Unknown" + fi + + # Package counts + apt_count=$(dpkg -l 2>/dev/null | grep -c "^ii" 2>/dev/null) || apt_count="0" + snap_count=$(snap list 2>/dev/null | tail -n +2 | wc -l 2>/dev/null) || snap_count="0" + flatpak_count=$(flatpak list --app 2>/dev/null | wc -l 2>/dev/null) || flatpak_count="0" + + # Process information + process_count=$(ps aux 2>/dev/null | wc -l 2>/dev/null) || process_count="Unknown" + [[ "$process_count" != "Unknown" ]] && process_count=$((process_count - 1)) + zombie_count=$(ps aux 2>/dev/null | awk '$8 ~ /^Z/ {count++} END {print count+0}' 2>/dev/null) || zombie_count="0" + + # System status + boot_time=$(who -b 2>/dev/null | awk '{print $3 " " $4}' 2>/dev/null) || boot_time="Unknown" + load_avg=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' 2>/dev/null | sed 's/^ *//' 2>/dev/null) || load_avg="Unknown" + + # Ultrabunt package statistics + local total_pkgs=${#PACKAGES[@]} + local installed_count=0 + for name in "${!PACKAGES[@]}"; do + if is_package_installed "$name" 2>/dev/null; then + ((installed_count++)) 2>/dev/null || true + fi + done 2>/dev/null || true + + # Ensure all variables are initialized with fallback values + [[ -z "$cpu_model" ]] && cpu_model="Unknown" + [[ -z "$cpu_cores" ]] && cpu_cores="Unknown" + [[ -z "$cpu_threads" ]] && cpu_threads="Unknown" + [[ -z "$cpu_freq" ]] && cpu_freq="Unknown" + [[ -z "$cpu_max_freq" ]] && cpu_max_freq="Unknown" + [[ -z "$cpu_family" ]] && cpu_family="Unknown" + [[ -z "$memory_used" ]] && memory_used="Unknown" + [[ -z "$memory_total" ]] && memory_total="Unknown" + [[ -z "$memory_type" ]] && memory_type="Unknown" + [[ -z "$memory_speed" ]] && memory_speed="Unknown" + [[ -z "$memory_slots" ]] && memory_slots="Unknown" + [[ -z "$storage_used" ]] && storage_used="Unknown" + [[ -z "$storage_total" ]] && storage_total="Unknown" + [[ -z "$storage_usage_percent" ]] && storage_usage_percent="Unknown" + [[ -z "$storage_devices" ]] && storage_devices="Unknown" + [[ -z "$storage_type" ]] && storage_type="Unknown" + [[ -z "$usb_ports_list" ]] && usb_ports_list="Unknown" + [[ -z "$display_ports_list" ]] && display_ports_list="Unknown" + [[ -z "$thunderbolt_count" ]] && thunderbolt_count="0" + [[ -z "$audio_ports_list" ]] && audio_ports_list="Unknown" + [[ -z "$internet_status" ]] && internet_status="Unknown" + [[ -z "$wifi_adapter" ]] && wifi_adapter="Unknown" + [[ -z "$eth_adapter" ]] && eth_adapter="Unknown" + [[ -z "$gpu_info" ]] && gpu_info="Unknown" + [[ -z "$audio_drivers" ]] && audio_drivers="Unknown" + [[ -z "$audio_devices" ]] && audio_devices="Unknown" + [[ -z "$bios_vendor" ]] && bios_vendor="Unknown" + [[ -z "$bios_version" ]] && bios_version="Unknown" + [[ -z "$bios_date" ]] && bios_date="Unknown" + [[ -z "$mb_manufacturer" ]] && mb_manufacturer="Unknown" + [[ -z "$mb_product" ]] && mb_product="Unknown" + [[ -z "$mb_version" ]] && mb_version="Unknown" + [[ -z "$mb_serial" ]] && mb_serial="Unknown" + [[ -z "$apt_count" ]] && apt_count="0" + [[ -z "$snap_count" ]] && snap_count="0" + [[ -z "$flatpak_count" ]] && flatpak_count="0" + [[ -z "$process_count" ]] && process_count="Unknown" + [[ -z "$zombie_count" ]] && zombie_count="0" + [[ -z "$boot_time" ]] && boot_time="Unknown" + [[ -z "$load_avg" ]] && load_avg="Unknown" + [[ -z "$uptime_info" ]] && uptime_info="Unknown" + [[ -z "$total_pkgs" ]] && total_pkgs="0" + [[ -z "$installed_count" ]] && installed_count="0" + + # Create compact overview text with single-line format + local overview="" + [[ -n "$sudo_warning" ]] && overview+="$sudo_warning" + + overview+="SYSTEM INFORMATION (Compact View)\n" + overview+="═══════════════════════════════════════\n" + + # Compact single-line format as requested + overview+="OS: $os_name \n" + overview+="Kernel: $kernel_version \n" + overview+="CPU: $cpu_model \n" + overview+="Cores: $cpu_cores | Threads: $cpu_threads \n" + overview+="Current: $cpu_freq" + [[ "$cpu_max_freq" != "Unknown" ]] && overview+=" | Max: $cpu_max_freq" + overview+=" \n" + overview+="RAM: $memory_used / $memory_total used" + [[ "$memory_type" != "Unknown" ]] && overview+=" ($memory_type" + [[ "$memory_speed" != "Unknown" ]] && overview+=" @ $memory_speed" + [[ "$memory_slots" != "Unknown" ]] && overview+=" - $memory_slots slots" + [[ "$memory_type" != "Unknown" ]] && overview+=")" + overview+=" \n" + overview+="Storage: $storage_used / $storage_total used ($storage_usage_percent)" + [[ "$storage_devices" != "Unknown" ]] && overview+=" - $storage_devices" + [[ "$storage_type" != "Unknown" ]] && overview+=" ($storage_type)" + overview+=" \n" + overview+="USB Ports: $usb_ports_list \n" + overview+="Display Ports: $display_ports_list \n" + [[ "$thunderbolt_count" -gt 0 ]] && overview+="Thunderbolt: ${thunderbolt_count} x Thunderbolt \n" + [[ -n "$audio_ports_list" ]] && overview+="Audio Ports: $audio_ports_list \n" + overview+="Internet: $internet_status \n" + overview+="Wi-Fi: $wifi_adapter \n" + overview+="Ethernet: $eth_adapter \n" + overview+="Graphics: $gpu_info \n" + overview+="Audio Driver: $audio_drivers \n" + [[ "$audio_devices" != "Unknown" ]] && overview+="Audio Devices: $audio_devices \n" + overview+="BIOS Vendor: $bios_vendor \n" + overview+="BIOS Version: $bios_version \n" + overview+="BIOS Date: $bios_date \n" + overview+="Motherboard: $mb_manufacturer $mb_product \n" + [[ "$mb_version" != "Unknown" ]] && overview+="MB Version: $mb_version \n" + [[ "$mb_serial" != "Unknown" ]] && overview+="MB Serial: $mb_serial \n" + overview+="\n" + overview+="Select a category below for detailed information:\n" + + # Build menu items + menu_items+=("cpu" "🖥️ CPU & Processor Details") + menu_items+=("memory" "🧠 Memory & RAM Information") + menu_items+=("storage" "💾 Storage & Disk Details") + menu_items+=("hardware" "⚙️ Hardware & System Info") + menu_items+=("network" "🌐 Network & Connectivity") + menu_items+=("graphics" "🎮 Graphics & Display Info") + menu_items+=("audio" "🔊 Audio & Sound Devices") + menu_items+=("packages" "📦 Package Managers & Software") + menu_items+=("zback" "(B) Back to Main Menu") + + local choice + choice=$(ui_menu "System Information Hub" "$overview" 50 140 8 "${menu_items[@]}") || break + + case "$choice" in + cpu) + show_cpu_details + ;; + memory) + show_memory_details + ;; + storage) + show_storage_details + ;; + hardware) + show_hardware_details + ;; + network) + show_network_details + ;; + graphics) + show_graphics_details + ;; + audio) + show_audio_details + ;; + packages) + show_package_details + ;; + zback|back|""|q|z) + break + ;; + esac + done + + log "Enhanced show_system_info completed" return 0 } @@ -7883,6 +9813,65 @@ export_package_list() { # ADVANCED FEATURES # ============================================================================== +show_essentials_setup_menu() { + log "Entering show_essentials_setup_menu" + + while true; do + local menu_items=( + "install" "(.Y.) Install Essentials (curl, wget, zip, unzip, htop, snapd)" + "" "(_*_)" + "back" "(Z) ← Back to Main Menu" + ) + + local choice + choice=$(ui_menu "First-Time Essentials" \ + "Install minimal tools to continue." \ + $DIALOG_HEIGHT $DIALOG_WIDTH $DIALOG_MENU_HEIGHT "${menu_items[@]}") || break + + case "$choice" in + install) + install_essentials_bundle || ui_msg "Install Error" "Failed to install essentials. Check $LOGFILE" + ;; + back|zback|z|"") + break + ;; + esac + done +} + +install_essentials_bundle() { + log "Installing First-Time Essentials bundle" + ui_msg "Essentials" "Installing core tools: curl, wget, zip, unzip, htop\n\nThis may take a minute." + + apt_update + + # Ensure APT prerequisites for repository keys + install_apt_package "ca-certificates" || true + install_apt_package "gnupg" || true + + # Install core tools + local tools=(curl wget zip unzip htop) + local failures=() + for t in "${tools[@]}"; do + if ! install_apt_package "$t"; then + failures+=("$t") + fi + done + + # Prepare snapd + if ! ensure_snapd_ready; then + ui_msg "snapd Issue" "snapd could not be prepared. You can retry from this menu." + fi + + if [[ ${#failures[@]} -gt 0 ]]; then + ui_msg "Install Partial" "Some tools failed to install:\n\n${failures[*]}\n\nPlease check the log: $LOGFILE" + return 1 + else + ui_msg "Essentials Installed" "All core tools installed and snapd prepared." + return 0 + fi +} + install_oh_my_zsh() { if [[ -d "$HOME/.oh-my-zsh" ]]; then ui_msg "Already Installed" "Oh My Zsh is already installed!" @@ -7910,6 +9899,194 @@ install_oh_my_zsh() { # WORDPRESS INSTALLATION SYSTEM # ============================================================================== +# ============================================================================== +# CLOUDFLARE FIREWALL SETUP +# ============================================================================ + +cloudflare_check_dependencies() { + local missing=() + if ! command -v curl >/dev/null 2>&1; then missing+=("curl"); fi + if ! command -v jq >/dev/null 2>&1; then missing+=("jq"); fi + if [[ ${#missing[@]} -gt 0 ]]; then + local list="${missing[*]}" + speak_if_enabled "Cloudflare setup requires $list." + if ui_yesno "Install Dependencies" "Cloudflare setup requires: ${list}\n\nInstall them now via apt?"; then + sudo apt-get update -y || true + sudo apt-get install -y ${list} || { + ui_msg "Install Failed" "Failed to install dependencies: ${list}. Please install manually and retry." + return 1 + } + else + ui_msg "Missing Dependencies" "Cannot continue without: ${list}." + return 1 + fi + fi +} + +cloudflare_create_rule() { + local zone_id="$1" + local api_token="$2" + local desc="$3" + local expr="$4" + local action="block" + local api="https://api.cloudflare.com/client/v4/zones/${zone_id}/firewall/rules" + + # Build JSON payload safely + local payload + payload=$(printf '[{"description":"%s","action":"%s","filter":{"expression":"%s"}}]' \ + "$desc" "$action" "$expr") + + speak_if_enabled "Creating Cloudflare rule: $desc" + log "Cloudflare POST payload: $payload" + local resp + resp=$(curl -s -X POST "$api" \ + -H "Authorization: Bearer ${api_token}" \ + -H "Content-Type: application/json" \ + --data "$payload") + log "Cloudflare response: $resp" + + local ok + ok=$(echo "$resp" | jq -r '.success // false' 2>/dev/null) + if [[ "$ok" == "true" ]]; then + ui_msg "Cloudflare" "✅ Rule created: ${desc}" + return 0 + else + local errs + errs=$(echo "$resp" | jq -r '.errors | map(.message) | join("; ")' 2>/dev/null) + ui_info "Cloudflare API Error" "Failed to create rule: ${desc}\n\nErrors: ${errs}\n\nFull Response:\n$(echo "$resp" | jq . 2>/dev/null || echo "$resp")" + return 1 + fi +} + +show_cloudflare_setup_menu() { + log "Entering show_cloudflare_setup_menu" + + # Ensure deps + cloudflare_check_dependencies || return 1 + + # Minimal instructions (Quick Guide) + local cf_quick + cf_quick="Cloudflare Setup — Quick Guide\n\n" + cf_quick+="Dependencies: curl + jq (auto-install if missing).\n\n" + cf_quick+="Zone ID: Cloudflare Dashboard → your domain → Overview → API → Zone ID.\n" + cf_quick+="API Token: My Profile → API Tokens → Create Custom Token.\n" + cf_quick+="Permissions: Zone → Firewall Services → Edit; Zone Resources → Specific Zone (your domain).\n" + cf_quick+="Optional: Verify token works:\n" + cf_quick+="curl -X GET https://api.cloudflare.com/client/v4/user/tokens/verify \\\n+ -H 'Authorization: Bearer YOUR_API_TOKEN' -H 'Content-Type: application/json'\n\n" + cf_quick+="You will enter: Zone ID, API Token, Domain.\n" + cf_quick+="Then choose: light/moderate/extreme, and optional bot blocking.\n" + cf_quick+="This will create the corresponding firewall rules." + ui_info "Cloudflare Setup (Minimal Instructions)" "$cf_quick" + + # Collect inputs + local cf_email zone_id api_token domain level botblock + cf_email=$(ui_input "Cloudflare Email (optional)" "Enter your Cloudflare email (optional, for logs only):" "") || return + zone_id=$(ui_input "Cloudflare Zone ID" "Enter your Cloudflare Zone ID:" "") || return + api_token=$(ui_input "Cloudflare API Token" "Enter your Cloudflare API Token (must have Firewall Rules permissions):" "") || return + domain=$(ui_input "Domain Name" "Enter your domain name (e.g. example.com):" "") || return + + if [[ -z "$zone_id" || -z "$api_token" || -z "$domain" ]]; then + ui_msg "Missing Inputs" "Zone ID, API Token, and Domain are required." + return 1 + fi + + # Choose security level + level=$(ui_menu "Security Level" \ + "Choose Cloudflare security level:" 12 60 6 \ + "light" "🟢 Light (basic protection)" \ + "moderate" "🟡 Moderate (adds wp-admin/wp-login limits)" \ + "extreme" "🔴 Extreme (adds country blocks)") || return + + # Bot blocking? + if ui_yesno "Advanced Bot Blocking" "Enable advanced bot blocking (AI & SEO bots)?"; then + botblock="y" + else + botblock="n" + fi + + speak_if_enabled "Applying Cloudflare firewall rules" + ui_msg "Applying Rules" "Applying Cloudflare firewall rules for ${domain} (${level}).\n\nThis may take a few seconds..." + + # Build expressions from instructions + local expr_light expr_moderate expr_extreme expr_bots + expr_light=$(printf '(http.request.uri.path contains "/xmlrpc.php") or ((http.request.uri.path eq "/wp-content/" or http.request.uri.path eq "/wp-includes/") and not http.referer contains "%s")' "$domain") + + expr_moderate='( + http.request.uri.path contains "/wp-login.php" or + (http.request.uri.path contains "/wp-admin/" and http.request.uri.path ne "/wp-admin/admin-ajax.php") +) +and not (ip.geoip.country in {"AU" "US" "UK"})' + + expr_extreme='( + ip.geoip.country in {"RU" "CN" "KP" "IR" "VN" "TR" "IN" "NG"} +) +and not cf.client.bot' + + expr_bots='(http.request.uri.path ne "/robots.txt") and ( + (http.user_agent contains "SemrushBot") or + (http.user_agent contains "AhrefsBot") or + (http.user_agent contains "Barkrowler") or + (http.user_agent contains "MJ12bot") or + (http.user_agent contains "DotBot") or + (http.user_agent contains "Exabot") or + (http.user_agent contains "Sogou") or + (http.user_agent contains "YandexBot") or + (http.user_agent contains "Baidu") or + (http.user_agent contains "ia_archiver") or + (http.user_agent contains "SemanticaBot") or + (http.user_agent contains "SiteLockSpider") or + (http.user_agent contains "SeznamBot") or + (http.user_agent contains "OpenLinkProfiler") or + (http.user_agent contains "SiteBot") or + (http.user_agent contains "Screaming Frog") or + (http.user_agent contains "DataForSeoBot") or + (http.user_agent contains "SEOkicks") or + (http.user_agent contains "BLEXBot") or + (http.user_agent contains "MegaIndex") or + (http.user_agent contains "MegaIndex.ru") or + (http.user_agent contains "NetcraftSurveyAgent") or + (http.user_agent contains "Apache-HttpClient") or + (http.user_agent contains "Python-urllib") or + (http.user_agent contains "python-requests") or + (http.user_agent contains "libwww-perl") or + (http.user_agent contains "Curl") or + (http.user_agent contains "wget") or + (http.user_agent contains "CensysInspect") or + (http.user_agent contains "ZoominfoBot") or + (http.user_agent contains "MauiBot") or + (http.user_agent contains "spbot") or + (http.user_agent contains "GPTBot") or + (http.user_agent contains "ClaudeBot") or + (http.user_agent contains "Claude-SearchBot") or + (http.user_agent contains "Claude-User") or + (http.user_agent contains "Bytespider") or + (http.user_agent contains "MistralAI-User") or + (http.user_agent contains "Perplexity-User") or + (http.user_agent contains "ProRataInc") or + (http.user_agent contains "Novellum") or + (http.user_agent contains "OAI-SearchBot") or + (http.user_agent contains "Meta-ExternalFetcher") or + (http.user_agent contains "Meta-ExternalAgent") +)' + + # Apply rules based on level + cloudflare_create_rule "$zone_id" "$api_token" "Block XMLRPC and unauthorized includes" "$expr_light" || return 1 + if [[ "$level" == "moderate" || "$level" == "extreme" ]]; then + cloudflare_create_rule "$zone_id" "$api_token" "Restrict wp-login/wp-admin to AU, US, UK" "$expr_moderate" || return 1 + fi + if [[ "$level" == "extreme" ]]; then + cloudflare_create_rule "$zone_id" "$api_token" "Block access from high-risk countries" "$expr_extreme" || return 1 + fi + if [[ "$botblock" =~ ^[Yy]$ ]]; then + cloudflare_create_rule "$zone_id" "$api_token" "AI Crawl Control - Block AI & SEO Bots" "$expr_bots" || return 1 + fi + + log "Cloudflare rules applied for domain: $domain level: $level" + speak_if_enabled "Cloudflare security rules applied" + ui_msg "Success" "✅ Cloudflare security rules applied for ${domain} (${level} mode)" + return 0 +} + show_wordpress_setup_menu() { log "Entering show_wordpress_setup_menu function" @@ -7928,7 +10105,7 @@ show_wordpress_setup_menu() { "wp-cli" "(.Y.) 🔧 Install/Manage WP-CLI" "wp-cleanup" "(.Y.) 🧹 WordPress Cleanup & Customization" "sql-import-optimize" "(.Y.) 📊 Optimize for Large SQL Imports" - "ssl-setup" "(.Y.) 🔒 Add SSL Certificate (Let's Encrypt)" + "ssl-setup" "(.Y.) 🔒 SSL Setup (Local or Live)" "wp-security" "(.Y.) 🛡️ WordPress Security Hardening" "" "(_*_)" "zback" "(Z) ← Back to Main Menu" @@ -7968,7 +10145,7 @@ show_wordpress_setup_menu() { optimize_for_large_sql_imports ;; ssl-setup) - wordpress_ssl_setup + wordpress_ssl_setup_menu ;; wp-security) wordpress_security_hardening @@ -8119,6 +10296,8 @@ wordpress_quick_setup() { ui_msg "SSL Setup Warning" "⚠️ SSL setup failed, but site will work with HTTP.\n\nYour WordPress site is fully functional at: http://$domain\n\nYou can set up SSL later using:\n• WordPress management menu\n• Manual SSL configuration\n• Let's Encrypt (for public domains)" fi fi + + # Removed Let's Encrypt prompt during quick install; SSL can be configured later via WordPress SSL menu (Local or Live) ui_msg "Step 5 Complete" "✅ $web_server configured successfully!\n\n• Server block: Created\n• PHP processing: Enabled\n• Security headers: Set\n• Site: Active and running$(if [[ "$use_ssl" == true ]]; then echo "\n• SSL: HTTPS enabled"; fi)" @@ -8177,6 +10356,8 @@ wordpress_custom_setup() { else configure_apache_wordpress_custom "$domain" "$site_dir" fi + + # Removed Let's Encrypt prompt during custom install; SSL can be configured later via WordPress SSL menu (Local or Live) # Show completion show_wordpress_completion "$domain" "$db_info" @@ -9614,9 +11795,13 @@ wordpress_ssl_setup() { fi fi - # Get domain name + # Get domain name (allow optional argument) local domain - domain=$(ui_input "Domain Name" "Enter domain for SSL certificate:" "") || return + if [[ -n "$1" ]]; then + domain="$1" + else + domain=$(ui_input "Domain Name" "Enter domain for SSL certificate:" "") || return + fi if [[ -z "$domain" || "$domain" == "localhost" ]]; then ui_msg "Invalid Domain" "SSL certificates cannot be issued for localhost or empty domains.\n\nPlease use a real domain name." @@ -9661,7 +11846,7 @@ wordpress_ssl_setup() { ui_msg "Obtaining SSL Certificate" "Requesting SSL certificate from Let's Encrypt...\n\nThis may take a moment." if sudo certbot --${certbot_plugin} -d "$domain" -d "www.$domain" --email "$email" --agree-tos --non-interactive 2>&1 | tee -a "$LOGFILE"; then - ui_msg "SSL Success" "SSL certificate installed successfully!\n\nYour site is now available at:\nhttps://$domain\n\nCertbot will automatically renew the certificate." + ui_msg "SSL Success" "SSL certificate installed successfully!\n\nYour site is now available at:\nhttps://$domain\n\nCertbot will automatically renew the certificate." else ui_msg "SSL Error" "Failed to obtain SSL certificate. Please check:\n\n• Domain DNS points to this server\n• Port 80/443 are open\n• Domain is accessible from internet\n\nCheck logs for details." fi @@ -9677,20 +11862,36 @@ setup_local_ssl() { local ssl_dir="/etc/ssl/local" sudo mkdir -p "$ssl_dir" - # Generate self-signed certificate + # Generate local certificate (prefer mkcert if available) local cert_file="$ssl_dir/$domain.crt" local key_file="$ssl_dir/$domain.key" - # Create certificate with proper extensions for modern browsers - sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout "$key_file" \ - -out "$cert_file" \ - -subj "/C=US/ST=Local/L=Local/O=Local Development/CN=$domain" \ - -extensions v3_req \ - -config <(cat /etc/ssl/openssl.cnf <(printf "\n[v3_req]\nsubjectAltName=DNS:$domain,DNS:*.$domain,IP:127.0.0.1")) 2>/dev/null || { - log_error "Failed to generate SSL certificate" - return 1 - } + if command -v mkcert >/dev/null 2>&1; then + log "mkcert detected - generating local certificate" + if ! sudo mkcert -key-file "$key_file" -cert-file "$cert_file" "$domain" "www.$domain" 2>/dev/null; then + log_warning "mkcert failed, falling back to self-signed OpenSSL certificate" + sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$key_file" \ + -out "$cert_file" \ + -subj "/C=US/ST=Local/L=Local/O=Local Development/CN=$domain" \ + -extensions v3_req \ + -config <(cat /etc/ssl/openssl.cnf <(printf "\n[v3_req]\nsubjectAltName=DNS:$domain,DNS:*.$domain,IP:127.0.0.1")) 2>/dev/null || { + log_error "Failed to generate SSL certificate" + return 1 + } + fi + else + log "mkcert not found - generating self-signed OpenSSL certificate" + sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$key_file" \ + -out "$cert_file" \ + -subj "/C=US/ST=Local/L=Local/O=Local Development/CN=$domain" \ + -extensions v3_req \ + -config <(cat /etc/ssl/openssl.cnf <(printf "\n[v3_req]\nsubjectAltName=DNS:$domain,DNS:*.$domain,IP:127.0.0.1")) 2>/dev/null || { + log_error "Failed to generate SSL certificate" + return 1 + } + fi # Set proper permissions sudo chmod 600 "$key_file" @@ -9707,6 +11908,121 @@ setup_local_ssl() { return 0 } +# Dedicated SSL setup menu for existing WordPress installations +wordpress_ssl_setup_menu() { + log "Entering wordpress_ssl_setup_menu" + + while true; do + # Discover WordPress sites + local wp_sites=() + if [[ -d "/var/www" ]]; then + while IFS= read -r -d '' dir; do + local site_name + site_name=$(basename "$dir") + if [[ -f "$dir/wp-config.php" ]]; then + wp_sites+=("$site_name") + fi + done < <(find /var/www -maxdepth 1 -type d -print0 2>/dev/null) + fi + + local info="WordPress SSL Setup\n\n" + if [[ ${#wp_sites[@]} -eq 0 ]]; then + info+="No WordPress installations detected under /var/www.\n\nUse WordPress Installation to create a site first." + else + info+="Choose a site, then select Local or Live SSL:\n" + info+="═══════════════════════════════════════════════════════════\n" + info+="Local (Development): .localhost domains recommended → self-signed or mkcert.\n" + info+="Live (Production): Public domain via Let’s Encrypt with Certbot.\n\n" + info+="Live prerequisites:\n" + info+="• DNS points to this server (A/AAAA record)\n" + info+="• If using Cloudflare, set records to your server’s IP (proxied OK)\n" + info+="• Ports 80/443 open and reachable\n" + info+="• Web server active (Nginx or Apache)\n" + fi + + local menu_items=() + for site in "${wp_sites[@]}"; do + local ssl_icon="🔓" + if [[ -f "/etc/letsencrypt/live/$site/fullchain.pem" ]] || [[ -f "/etc/ssl/local/$site.crt" ]]; then + ssl_icon="🔒" + fi + menu_items+=("$site" "$ssl_icon $site") + done + menu_items+=("" "(_*)") + menu_items+=("zback" "(Z) ← Back to WordPress Menu") + + local choice + choice=$(ui_menu "SSL Setup" "$info" $DIALOG_HEIGHT $DIALOG_WIDTH $DIALOG_MENU_HEIGHT "${menu_items[@]}") || break + + case "$choice" in + back|zback|z|"") + break + ;; + *) + if [[ " ${wp_sites[*]} " =~ " $choice " ]]; then + wordpress_ssl_method_menu "$choice" + fi + ;; + esac + done + + log "Exiting wordpress_ssl_setup_menu" +} + +wordpress_ssl_method_menu() { + local domain="$1" + + # Detect web server for local SSL config + local web_server="" + if systemctl is-active --quiet nginx; then + web_server="nginx" + elif systemctl is-active --quiet apache2; then + web_server="apache2" + fi + + local info="SSL for: $domain\n\n" + if [[ -z "$web_server" ]]; then + info+="No active web server detected. Please start Nginx or Apache before configuring SSL." + else + info+="Choose SSL type:\n" + info+="═══════════════════════════════════════════════════════════\n" + info+="Local (Development): Generates a local certificate (mkcert if available, otherwise self-signed). Best for .localhost domains.\n\n" + info+="Live (Production): Obtains a certificate from Let’s Encrypt via Certbot.\n" + info+="Requirements: DNS points to this server, Cloudflare (if used) is set to your server IP, and ports 80/443 are open.\n" + fi + + local menu_items=( + "local" "🔒 Local SSL (self-signed/mkcert)" + "live" "🌍 Live SSL (Let’s Encrypt via Certbot)" + "" "(_*)" + "zback" "(Z) ← Back to Site List" + ) + + local choice + choice=$(ui_menu "Choose SSL Type" "$info" $DIALOG_HEIGHT $DIALOG_WIDTH 8 "${menu_items[@]}") || return + + case "$choice" in + local) + if [[ -z "$web_server" ]]; then + ui_error "No web server detected. Start Nginx or Apache first." + return + fi + ui_msg "Configuring Local SSL" "Generating a local certificate and updating $web_server configuration for $domain." + if setup_local_ssl "$domain" "$web_server"; then + ui_success "Local SSL configured: https://$domain" + else + ui_error "Local SSL setup failed for $domain" + fi + ;; + live) + # Route to Let’s Encrypt flow, passing domain + wordpress_ssl_setup "$domain" + ;; + back|zback|z|"") + ;; + esac +} + setup_nginx_ssl() { local domain="$1" local cert_file="$2" @@ -10339,6 +12655,7 @@ show_individual_site_management() { "test-db" "🗄️ Test Database Connection" "repair-db" "🔧 Repair Database Connection" "" "(_*_)" + "file-access" "📁 Grant File Access to Current User" "enable-site" "🔗 Enable/Disable Site" "test-site" "🧪 Test Site Accessibility" "" "(_*_)" @@ -10391,6 +12708,9 @@ show_individual_site_management() { repair-db) repair_wordpress_database_connection "$site" ;; + file-access) + grant_file_access_to_user "$site" + ;; enable-site) manage_site_enablement "$site" "$web_server" ;; @@ -10471,7 +12791,7 @@ test_site_accessibility() { fi # HTTPS test if SSL is configured - if [[ -f "/etc/letsencrypt/live/$site/fullchain.pem" ]] || [[ -f "/etc/ssl/certs/$site.crt" ]]; then + if [[ -f "/etc/letsencrypt/live/$site/fullchain.pem" ]] || [[ -f "/etc/ssl/certs/$site.crt" ]] || [[ -f "/etc/ssl/local/$site.crt" ]]; then test_info+="HTTPS Test:\n" local https_result if [[ "$site" =~ \.localhost$ ]]; then @@ -11012,6 +13332,103 @@ restart_web_services() { fi } +# WordPress file access management function +grant_file_access_to_user() { + local site="$1" + local current_user + current_user=$(whoami) + + log "Granting file access to user $current_user for WordPress site: $site" + + # Check if running as root + if [[ "$EUID" -ne 0 ]]; then + ui_error "Root Access Required" "This function requires root privileges to modify file permissions.\n\nPlease run the script with sudo or as root user." + return 1 + fi + + # Get the actual user (not root) if running with sudo + if [[ -n "$SUDO_USER" ]]; then + current_user="$SUDO_USER" + fi + + local info_text="File Access Management for WordPress Sites\n\n" + info_text+="This will grant the current user ($current_user) access to manage files in /var/www/\n\n" + info_text+="The following actions will be performed:\n" + info_text+="• Add user '$current_user' to the 'www-data' group\n" + info_text+="• Change ownership of /var/www to $current_user:www-data\n" + info_text+="• Set permissions to 2775 (rwxrwsr-x) for directories\n" + info_text+="• Set the setgid bit on directories for proper group inheritance\n" + info_text+="• Activate the www-data group for the current session\n\n" + info_text+="⚠️ This will affect ALL WordPress sites in /var/www/\n\n" + info_text+="Benefits:\n" + info_text+="✅ Edit files directly without sudo\n" + info_text+="✅ Proper file permissions maintained\n" + info_text+="✅ Shared access with web server (www-data)\n" + info_text+="✅ New files inherit correct group ownership\n" + + if ! ui_yesno "Grant File Access" "$info_text"; then + return 0 + fi + + local results="" + local success=true + + # Step 1: Add user to www-data group + results+="Adding user '$current_user' to www-data group...\n" + if usermod -aG www-data "$current_user" 2>/dev/null; then + results+="✅ User added to www-data group successfully\n\n" + else + results+="❌ Failed to add user to www-data group\n\n" + success=false + fi + + # Step 2: Change ownership of /var/www + results+="Changing ownership of /var/www to $current_user:www-data...\n" + if chown -R "$current_user:www-data" /var/www 2>/dev/null; then + results+="✅ Ownership changed successfully\n\n" + else + results+="❌ Failed to change ownership\n\n" + success=false + fi + + # Step 3: Set permissions + results+="Setting permissions to 2775 for /var/www...\n" + if chmod -R 2775 /var/www 2>/dev/null; then + results+="✅ Permissions set successfully\n\n" + else + results+="❌ Failed to set permissions\n\n" + success=false + fi + + # Step 4: Set setgid bit on directories + results+="Setting setgid bit on directories...\n" + if find /var/www -type d -exec chmod g+s {} \; 2>/dev/null; then + results+="✅ Setgid bit set on directories\n\n" + else + results+="❌ Failed to set setgid bit\n\n" + success=false + fi + + # Step 5: Information about group activation + results+="Group Activation:\n" + results+="⚠️ To activate the www-data group for your current session, run:\n" + results+=" newgrp www-data\n\n" + results+="Or log out and log back in for the group changes to take effect.\n\n" + + if [[ "$success" == "true" ]]; then + results+="🎉 File access granted successfully!\n\n" + results+="You can now:\n" + results+="• Edit files in /var/www/ without sudo\n" + results+="• Create new files with proper permissions\n" + results+="• Manage WordPress sites directly\n\n" + results+="Note: You may need to start a new shell session or run 'newgrp www-data' to activate the group membership." + + ui_info "File Access Granted" "$results" + else + ui_error "File Access Setup Failed" "$results" + fi +} + # WordPress database testing and repair functions test_wordpress_database_connection() { local site="$1" @@ -11686,7 +14103,8 @@ wordpress_wpcli_management() { "zback" "(Z) ← Back to WordPress Menu" ) - local choice=$(show_menu "WP-CLI Management" "$wpcli_status" "${menu_items[@]}") + local choice + choice=$(ui_menu "WP-CLI Management" "$wpcli_status" 20 70 10 "${menu_items[@]}") || break case $choice in install) @@ -11726,9 +14144,17 @@ install_wpcli() { fi fi + # Ensure PHP CLI is available + if ! command -v php >/dev/null 2>&1; then + log_error "PHP CLI (php) is required for WP-CLI" + ui_msg "WP-CLI Install Error" "WP-CLI requires the PHP command-line interpreter (php).\n\nPlease install it first: sudo apt-get update && sudo apt-get install -y php-cli" + return 1 + fi + # Download and install WP-CLI log "Downloading WP-CLI..." - if curl -O https://raw.githubusercontent.com/wp-cli/wp-cli/v2.8.1/utils/wp-cli.phar; then + # Use official builds URL and follow redirects + if curl -L -o wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; then log "Making WP-CLI executable..." chmod +x wp-cli.phar @@ -11753,7 +14179,7 @@ install_wpcli() { # Install bash completion (optional) log "Installing bash completion for WP-CLI..." - if curl -o /tmp/wp-completion.bash https://raw.githubusercontent.com/wp-cli/wp-cli/v2.8.1/utils/wp-completion.bash 2>/dev/null; then + if curl -L -o /tmp/wp-completion.bash https://raw.githubusercontent.com/wp-cli/wp-cli/main/utils/wp-completion.bash 2>/dev/null; then sudo mv /tmp/wp-completion.bash /etc/bash_completion.d/wp-cli log "Bash completion installed" else @@ -11774,9 +14200,13 @@ update_wpcli() { fi # Update using WP-CLI's self-update command - if wp cli update; then + local wp_cmd=(wp) + if [[ $EUID -eq 0 ]]; then + wp_cmd=(wp --allow-root) + fi + if "${wp_cmd[@]}" cli update; then log "✅ WP-CLI updated successfully!" - wp --version + "${wp_cmd[@]}" --version else log_warning "WP-CLI self-update failed. Trying manual update..." install_wpcli @@ -11854,19 +14284,38 @@ test_wpcli_sites() { cd "$site_dir" || continue - # Test basic WP-CLI functionality - if wp core version 2>/dev/null; then - echo " ✅ WP-CLI working - WordPress version: $(wp core version 2>/dev/null)" - - # Show additional info - echo " 📊 Database status: $(wp db check 2>/dev/null && echo "✅ OK" || echo "❌ Error")" - echo " 🔌 Active plugins: $(wp plugin list --status=active --format=count 2>/dev/null || echo "Unknown")" - echo " 🎨 Active theme: $(wp theme list --status=active --field=name 2>/dev/null || echo "Unknown")" + # Select the appropriate WP-CLI invocation based on user context + local wp_cmd=() + if [[ $EUID -eq 0 ]]; then + # Prefer running as www-data; fallback to allow-root + if sudo -u www-data -H wp --path="$site_dir" core version >/dev/null 2>&1; then + wp_cmd=(sudo -u www-data -H wp --path="$site_dir") + elif wp --allow-root --path="$site_dir" core version >/dev/null 2>&1; then + wp_cmd=(wp --allow-root --path="$site_dir") + else + echo " ❌ WP-CLI not working on this site" + echo " 💡 Try: sudo -u www-data -H wp --path=\"$site_dir\" core version" + echo " 💡 Or: wp --allow-root --path=\"$site_dir\" core version" + echo " 🔍 Checking permissions..." + ls -la wp-config.php 2>/dev/null || echo " ⚠️ wp-config.php not readable" + continue + fi else - echo " ❌ WP-CLI not working on this site" - echo " 🔍 Checking permissions..." - ls -la wp-config.php 2>/dev/null || echo " ⚠️ wp-config.php not readable" + if wp --path="$site_dir" core version >/dev/null 2>&1; then + wp_cmd=(wp --path="$site_dir") + else + echo " ❌ WP-CLI not working on this site" + echo " 💡 Try: sudo -u www-data -H wp --path=\"$site_dir\" core version" + echo " 🔍 Checking permissions..." + ls -la wp-config.php 2>/dev/null || echo " ⚠️ wp-config.php not readable" + continue + fi fi + + echo " ✅ WP-CLI working - WordPress version: $("${wp_cmd[@]}" core version 2>/dev/null)" + echo " 📊 Database status: $("${wp_cmd[@]}" db check >/dev/null 2>&1 && echo "✅ OK" || echo "❌ Error")" + echo " 🔌 Active plugins: $("${wp_cmd[@]}" plugin list --status=active --format=count 2>/dev/null || echo "Unknown")" + echo " 🎨 Active theme: $("${wp_cmd[@]}" theme list --status=active --field=name 2>/dev/null || echo "Unknown")" done echo @@ -12979,6 +15428,185 @@ show_database_management_menu() { log "Exiting show_database_management_menu function" } +# ============================================================================== +# SWAPPINESS TUNING +# ============================================================================== + +get_runtime_swappiness() { + local value="" + if command -v sysctl >/dev/null 2>&1; then + value=$(sysctl -n vm.swappiness 2>/dev/null || true) + fi + if [[ -z "$value" && -r /proc/sys/vm/swappiness ]]; then + value=$(cat /proc/sys/vm/swappiness 2>/dev/null || true) + fi + echo "${value:-unknown}" +} + +get_persistent_swappiness() { + local file="/etc/sysctl.conf" + if [[ ! -r "$file" ]]; then + echo "" + return 0 + fi + local line + line=$(grep -E '^[[:space:]]*vm\.swappiness[[:space:]]*=' "$file" | grep -v '^[[:space:]]*#' | tail -n 1) + if [[ -z "$line" ]]; then + echo "" + else + echo "$line" | sed -E 's/^[[:space:]]*vm\.swappiness[[:space:]]*=[[:space:]]*([0-9]+).*/\1/' + fi +} + +validate_swappiness_value() { + local v="$1" + if [[ "$v" =~ ^[0-9]+$ ]] && (( v >= 0 && v <= 100 )); then + return 0 + fi + return 1 +} + +set_runtime_swappiness() { + local v="$1" + if ! validate_swappiness_value "$v"; then + ui_msg "Invalid Value" "Please enter a number between 0 and 100." + return 1 + fi + if ! command -v sysctl >/dev/null 2>&1; then + log_error "sysctl command not found" + ui_msg "Error" "sysctl command not found on this system." + return 1 + fi + if sudo sysctl vm.swappiness="$v" >/dev/null 2>&1; then + ui_msg "Runtime Updated" "Swappiness set to $v (runtime).\nCurrent runtime value: $(get_runtime_swappiness)" + return 0 + else + log_error "Failed to set runtime swappiness" + ui_msg "Error" "Failed to set runtime swappiness. See /var/log/ultrabunt.log for details." + return 1 + fi +} + +ensure_persistent_swappiness() { + local v="$1" + if ! validate_swappiness_value "$v"; then + ui_msg "Invalid Value" "Please enter a number between 0 and 100." + return 1 + fi + local file="/etc/sysctl.conf" + if [[ ! -f "$file" ]]; then + echo "vm.swappiness=$v" | sudo tee "$file" >/dev/null + else + if grep -Eq '^[[:space:]]*#?[[:space:]]*vm\\.swappiness[[:space:]]*=' "$file"; then + sudo sed -i -E 's/^[[:space:]]*#?[[:space:]]*vm\\.swappiness[[:space:]]*=.*/vm.swappiness='"$v"'/g' "$file" + else + echo "vm.swappiness=$v" | sudo tee -a "$file" >/dev/null + fi + fi + return 0 +} + +reload_sysctl_conf() { + if sudo sysctl -p /etc/sysctl.conf >/dev/null 2>&1; then + ui_msg "Sysctl Reloaded" "sysctl.conf reloaded.\nCurrent runtime value: $(get_runtime_swappiness)" + return 0 + else + log_warning "Failed to reload sysctl.conf" + ui_msg "Error" "Failed to reload /etc/sysctl.conf." + return 1 + fi +} + +show_sysctl_swappiness_lines() { + local file="/etc/sysctl.conf" + local lines + if [[ -r "$file" ]]; then + lines=$(grep -n -E 'vm\\.swappiness' "$file" 2>/dev/null) + fi + ui_info "sysctl.conf swappiness" "${lines:-No swappiness entry found in /etc/sysctl.conf}" +} + +show_swappiness_menu() { + log "Entering show_swappiness_menu" + while true; do + local runtime_val persistent_val entry_state + runtime_val=$(get_runtime_swappiness) + persistent_val=$(get_persistent_swappiness) + if [[ -n "$persistent_val" ]]; then + entry_state="Persistent entry present (vm.swappiness=$persistent_val)" + else + entry_state="No persistent entry in /etc/sysctl.conf" + fi + + local info="Swappiness Controls\n\n" + info+="Runtime value: ${runtime_val}\n" + info+="Persistent (/etc/sysctl.conf): ${persistent_val:-none}\n" + info+="State: ${entry_state}\n\nChoose an action:" + + local menu_items=( + "set-10" "(.Y.) Set runtime to 10" + "set-30" "(.Y.) Set runtime to 30" + "set-40" "(.Y.) Set runtime to 40" + "set-custom" "(.Y.) Set runtime to custom value" + "" "(_*_)" + "persist-10" "(.Y.) Persist swappiness 10" + "persist-30" "(.Y.) Persist swappiness 30" + "persist-40" "(.Y.) Persist swappiness 40" + "persist-custom" "(.Y.) Persist custom value" + "apply-persist" "(.Y.) Reload sysctl.conf (apply persistent values)" + "show-entry" "(.Y.) Show sysctl.conf swappiness lines" + "edit-conf" "(.Y.) Edit /etc/sysctl.conf" + "" "(_*_)" + "zback" "(Z) ← Back to Main Menu" + ) + + local choice + choice=$(ui_menu "Swappiness Tuning" "$info" 25 90 15 "${menu_items[@]}") || break + + case "$choice" in + set-10) set_runtime_swappiness 10 ;; + set-30) set_runtime_swappiness 30 ;; + set-40) set_runtime_swappiness 40 ;; + set-custom) + local val + val=$(ui_input "Custom Swappiness" "Enter a value between 0 and 100:" "${runtime_val:-60}") || continue + [[ -z "$val" ]] && continue + set_runtime_swappiness "$val" + ;; + persist-10) + ensure_persistent_swappiness 10 && ui_msg "Persistent Updated" "vm.swappiness set to 10 in /etc/sysctl.conf" + ;; + persist-30) + ensure_persistent_swappiness 30 && ui_msg "Persistent Updated" "vm.swappiness set to 30 in /etc/sysctl.conf" + ;; + persist-40) + ensure_persistent_swappiness 40 && ui_msg "Persistent Updated" "vm.swappiness set to 40 in /etc/sysctl.conf" + ;; + persist-custom) + local pval + pval=$(ui_input "Custom Persistent Value" "Enter a value between 0 and 100:" "${persistent_val:-60}") || continue + [[ -z "$pval" ]] && continue + if ensure_persistent_swappiness "$pval"; then + ui_msg "Persistent Updated" "vm.swappiness set to $pval in /etc/sysctl.conf" + fi + ;; + apply-persist) + reload_sysctl_conf + ;; + show-entry) + show_sysctl_swappiness_lines + ;; + edit-conf) + sudo nano /etc/sysctl.conf + ;; + zback|back|"") + break + ;; + esac + done + log "Exiting show_swappiness_menu" +} + # LOG VIEWER FUNCTIONS show_log_viewer_menu() { log "Entering show_log_viewer_menu function" @@ -13714,9 +16342,9 @@ main() { ensure_deps log "Dependencies check completed" - log "╔═══════════════════════════════════════════════════════╗" - log "║ ULTRABUNT ULTIMATE BUNTSTALLER v4.2.0 STARTED ║" - log "╚═══════════════════════════════════════════════════════╝" + log "╔═══════════════════════════════════════════════╗" + log "║ ULTRABUNT ULTIMATE BUNTSTALLER v4.2.0 STARTED ║" + log "╚═══════════════════════════════════════════════╝" # Update buntage cache log "Starting APT cache update..."