├── LICENSE ├── linkedin-article.md ├── readme.md └── universal-os-detector.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Marcus Deacey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /linkedin-article.md: -------------------------------------------------------------------------------- 1 | # Creating a Universal OS Detection Script: Solving a Common Developer Challenge 2 | Linkedin article link: https://www.linkedin.com/pulse/creating-universal-os-detection-script-solving-common-marcus-deacey-6sbcc/ 3 | 4 | ## Introduction 5 | 6 | As developers, we often face the challenge of creating scripts that need to work across multiple operating systems. One of the most common tasks is detecting the OS on which a script is running. While there are many solutions available online, most are incomplete or tailored to specific use cases. This article explores the creation of a universal OS detection script that addresses these limitations and provides a robust solution for developers. 7 | 8 | ## The Problem 9 | 10 | A quick search for "bash detect OS" on Stack Overflow reveals a popular question with over 800 upvotes and 500,000 views. The answers provide various methods, but none offer a comprehensive solution that works universally across different environments. This fragmentation leads to developers cobbling together different approaches, often resulting in scripts that are brittle or fail in edge cases. 11 | 12 | ## The Solution: A Universal OS Detection Script 13 | 14 | To address this common challenge, we've created a comprehensive Bash script that not only detects the operating system but also provides additional useful information about the environment. Let's break down the key components of this script: 15 | 16 | 1. **Robust Error Handling**: The script uses `set -eo pipefail` and trap commands to catch and report errors, ensuring that issues are not silently ignored. 17 | 18 | ```bash 19 | set -eo pipefail 20 | trap 'log "Error encountered at line ${LINENO:-unknown} while executing command: ${BASH_COMMAND:-unknown}" "ERROR"' ERR 21 | ``` 22 | 23 | 2. **Comprehensive Logging**: A flexible logging system is implemented, allowing for different log levels (ERROR, WARN, INFO, SYSTEM, DEBUG) and optional debug output. 24 | 25 | ```bash 26 | log() { 27 | local message="${1:-}" 28 | local level="${2:-INFO}" 29 | local current_time=$(date '+%Y-%m-%d %H:%M:%S') 30 | 31 | local color 32 | case "$level" in 33 | ERROR) color=$COLOR_ERROR ;; 34 | WARN) color=$COLOR_WARN ;; 35 | INFO) color=$COLOR_INFO ;; 36 | SYSTEM) color=$COLOR_SYSTEM ;; 37 | DEBUG) color=$COLOR_RESET ;; 38 | *) color=$COLOR_RESET ;; 39 | esac 40 | 41 | echo -e "$current_time [$level]: $message" >> "$LOG_FILE" 42 | 43 | if [ "$DEBUG_MODE" -eq 1 ] || [ "$level" = "SYSTEM" ] || [ "$level" = "ERROR" ]; then 44 | echo -e "${color}$current_time [$level]: $message${COLOR_RESET}" 45 | fi 46 | } 47 | ``` 48 | 49 | 3. **Container Detection**: The script checks for common indicators of containerized environments, such as Docker or LXC. 50 | 51 | ```bash 52 | detect_container() { 53 | log "Detecting container environment..." INFO 54 | if [ -f /.dockerenv ]; then 55 | log "Running inside Docker" SYSTEM 56 | elif grep -q 'docker\|lxc' /proc/1/cgroup 2>/dev/null; then 57 | log "Running inside a container (Docker/LXC)" SYSTEM 58 | elif grep -q 'VxID' /proc/self/status 2>/dev/null; then 59 | log "Running inside OpenVZ" SYSTEM 60 | else 61 | log "Not running inside a container" SYSTEM 62 | fi 63 | } 64 | ``` 65 | 66 | 4. **OS Detection**: Using a combination of `uname` and `$OSTYPE`, the script can accurately identify a wide range of operating systems, including macOS, Linux, FreeBSD, Windows (via Cygwin/MSYS), Solaris, and AIX. 67 | 68 | ```bash 69 | detect_os() { 70 | log "Detecting operating system..." INFO 71 | OS=$(uname || echo "Unknown OS") 72 | OSTYPE="${OSTYPE:-$(uname)}" 73 | 74 | case "$(lowercase "$OSTYPE")" in 75 | darwin*) OS="MacOS" ;; 76 | linux*) OS="Linux" ;; 77 | freebsd*) OS="FreeBSD" ;; 78 | cygwin*|msys*|mingw*) OS="Windows" ;; 79 | solaris*) OS="Solaris" ;; 80 | aix*) OS="AIX" ;; 81 | *) OS="Unknown" log "Unknown OS detected" WARN ;; 82 | esac 83 | 84 | log "Operating System: $OS" SYSTEM 85 | } 86 | ``` 87 | 88 | 5. **Architecture Detection**: The script identifies the system architecture, covering common types like x86, ARM, PowerPC, and RISC-V. 89 | 90 | ```bash 91 | detect_arch() { 92 | log "Detecting architecture..." INFO 93 | ARCH=$(uname -m || echo "Unknown architecture") 94 | 95 | case "$ARCH" in 96 | x86_64) ARCH="x86_64 (64-bit)" ;; 97 | i*86) ARCH="x86 (32-bit)" ;; 98 | armv6l|armv7l) ARCH="ARM (32-bit)" ;; 99 | aarch64) ARCH="ARM (64-bit)" ;; 100 | ppc64le) ARCH="PowerPC 64-bit (little-endian)" ;; 101 | riscv64) ARCH="RISC-V (64-bit)" ;; 102 | *) ARCH="Unknown Architecture" log "Unknown architecture detected: $ARCH" WARN ;; 103 | esac 104 | 105 | log "Architecture: $ARCH" SYSTEM 106 | } 107 | ``` 108 | 109 | 6. **Kernel Version**: For Unix-like systems, the kernel version is also reported. 110 | 111 | ```bash 112 | detect_kernel() { 113 | log "Detecting kernel version..." INFO 114 | KERNEL=$(uname -r || echo "Unknown kernel") 115 | log "Kernel: $KERNEL" SYSTEM 116 | } 117 | ``` 118 | 119 | 7. **Function Tests**: Before running the main detection routines, the script checks for the availability of required commands and file access permissions. 120 | 121 | ```bash 122 | function_tests() { 123 | log "Running function tests..." INFO 124 | log "Checking command availability..." INFO 125 | check_command "uname" || exit 1 126 | check_command "tr" || exit 1 127 | check_command "grep" || exit 1 128 | check_file_access "$LOG_FILE" || exit 1 129 | log "All function tests passed." INFO 130 | } 131 | ``` 132 | 133 | 8. **Cleanup Routine**: A cleanup function ensures that temporary resources are properly removed upon script completion or interruption. 134 | 135 | ```bash 136 | cleanup() { 137 | log "Cleaning up resources..." INFO 138 | log "Removing log file..." INFO 139 | rm -f "$LOG_FILE".lock 140 | log "Cleanup completed" INFO 141 | log "Exiting..." INFO 142 | exit 0 143 | } 144 | 145 | trap 'log "Received termination signal. Exiting." "WARN"; cleanup' SIGINT SIGTERM 146 | ``` 147 | 148 | ## Key Features and Best Practices 149 | 150 | 1. **Portability**: The script uses POSIX-compliant commands and syntax, ensuring it works across different Unix-like systems. 151 | 152 | 2. **Security**: The script avoids using `eval` or other potentially dangerous constructs, prioritizing security. 153 | 154 | 3. **Flexibility**: Environment variables like `LOG_FILE` and `DEBUG_MODE` allow users to customize the script's behavior without modifying the code. 155 | 156 | ```bash 157 | LOG_FILE=${LOG_FILE:-~/universal-os-detector.log} 158 | DEBUG_MODE=${DEBUG_MODE:-0} 159 | ``` 160 | 161 | 4. **Comprehensive Detection**: By checking multiple system attributes, the script provides a more complete picture of the environment than simpler OS detection methods. 162 | 163 | 5. **Error Resilience**: The script includes error checking and reporting, making it easier to diagnose issues when they occur. 164 | 165 | ## Conclusion 166 | 167 | Creating a universal OS detection script addresses a common need in the developer community. By providing a comprehensive, robust, and flexible solution, we can save time and reduce errors in cross-platform development workflows. This script serves as a foundation that can be easily integrated into larger projects or used as a standalone tool for system information gathering. 168 | 169 | As the development landscape continues to evolve, with new operating systems and containerized environments emerging, having a reliable method for OS detection becomes increasingly crucial. This script aims to be that reliable tool, adaptable to future changes while solving today's challenges. 170 | 171 | The full script, along with documentation and usage instructions, is available on GitHub: 172 | 173 | [Universal OS Detector](https://github.com/mdeacey/universal-os-detector/tree/main) 174 | 175 | Feel free to use, modify, and contribute to this script. I encourage you to explore the repository, test the script in your own environments, and share your feedback. Your contributions can help enhance the tool and make it more robust for the entire development community. Together, we can build more reliable solutions for cross-platform development. 176 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Universal OS Detector 2 | 3 | Universal OS Detector is a Bash script designed to provide detailed information about the system it's running on. It aims to detect a broad range of operating systems, system architectures, kernel versions, desktop environments, and container environments, ensuring compatibility with as many systems as possible. The script is built to run effectively across different systems, providing universal detection capabilities. 4 | 5 | ## Features 6 | 7 | - Detects operating system and version/distribution 8 | - Identifies system architecture 9 | - Reports kernel version 10 | - Detects desktop environment 11 | - Checks for container environments (Docker, Podman, Kubernetes, LXC, OpenVZ) 12 | - Comprehensive logging with color-coded output and configurable log levels 13 | - Error handling and cleanup procedures 14 | - Functional tests to ensure script dependencies are met 15 | - Supports detection on mobile including Android and iOS 16 | 17 | ## Installation 18 | 19 | 1. Clone this repository or download the `universal-os-detector.sh` script. 20 | 2. Make the script executable: 21 | ``` 22 | chmod +x universal-os-detector.sh 23 | ``` 24 | 25 | ## Usage and Examples 26 | 27 | Run the script with: 28 | 29 | ``` 30 | ./universal-os-detector.sh 31 | ``` 32 | 33 | The script provides information about: 34 | - Container environment (if applicable) 35 | - Operating System and Version/Distribution 36 | - Desktop Environment 37 | - System Architecture 38 | - Kernel Version 39 | 40 | ### Basic Usage 41 | 42 | When run without any options, the script outputs only the system information: 43 | 44 | ``` 45 | $ ./universal-os-detector.sh 46 | 2024-09-19 14:14:15 [system]: Not running inside a container 47 | 2024-09-19 14:14:15 [system]: Operating System: MacOS 48 | 2024-09-19 14:14:15 [system]: Version: 14.0 (Sonoma) 49 | 2024-09-19 14:14:15 [system]: Desktop Environment: MacOS 50 | 2024-09-19 14:14:15 [system]: Architecture: x86_64 (64-bit) 51 | 2024-09-19 14:14:15 [system]: Kernel: 23.6.0 52 | ``` 53 | 54 | ### Configurable Log Levels 55 | 56 | You can set the console log level using the `console_log_level` environment variable: 57 | 58 | ``` 59 | console_log_level=3 ./universal-os-detector.sh 60 | ``` 61 | 62 | Log levels: 63 | - 0 or n/none: No console output 64 | - 1 or d/default: system, warn, and error messages (default) 65 | - 2 or v/verbose: system, warn, error, and info messages 66 | - 3 or deb/debug: All messages, including debug 67 | 68 | ### Verbose Log Level Example 69 | 70 | Here's an example of running the script with verbose logging (console_log_level=2): 71 | 72 | ``` 73 | $ console_log_level=v ./universal-os-detector.sh 74 | 2024-09-19 15:30:10 [info]: Console log level is set to: VERBOSE [2] 75 | 2024-09-19 15:30:10 [info]: Running functional tests... 76 | 2024-09-19 15:30:10 [info]: Checking command availability... 77 | 2024-09-19 15:30:10 [info]: Verifying READ/WRITE permissions for log file directory: /home/user... 78 | 2024-09-19 15:30:10 [info]: Verifying READ/WRITE permissions for log file: /home/user/universal-os-detector.log... 79 | 2024-09-19 15:30:10 [info]: All functional tests passed. 80 | 2024-09-19 15:30:10 [info]: Starting detection... 81 | 2024-09-19 15:30:10 [info]: Detecting container environment... 82 | 2024-09-19 15:30:10 [system]: Container Environment: None 83 | 2024-09-19 15:30:10 [info]: Detecting operating system... 84 | 2024-09-19 15:30:10 [system]: Operating System: Linux 85 | 2024-09-19 15:30:10 [info]: Detecting version or distribution... 86 | 2024-09-19 15:30:10 [system]: Distribution: Ubuntu 22.04 LTS 87 | 2024-09-19 15:30:10 [info]: Detecting desktop environment... 88 | 2024-09-19 15:30:10 [system]: Desktop Environment: GNOME 89 | 2024-09-19 15:30:10 [info]: Detecting architecture... 90 | 2024-09-19 15:30:10 [system]: Architecture: x86_64 (64-bit) 91 | 2024-09-19 15:30:10 [info]: Detecting kernel version... 92 | 2024-09-19 15:30:10 [system]: Kernel: 5.15.0-79-generic 93 | 2024-09-19 15:30:10 [info]: Detection completed. 94 | 2024-09-19 15:30:10 [info]: Cleaning up resources... 95 | 2024-09-19 15:30:10 [info]: Removing log file... 96 | 2024-09-19 15:30:10 [info]: Cleanup completed 97 | 2024-09-19 15:30:10 [info]: Exiting... 98 | ``` 99 | 100 | ### Custom Log File 101 | 102 | Specify a custom log file path: 103 | 104 | ``` 105 | LOG_FILE=/path/to/custom.log ./universal-os-detector.sh 106 | ``` 107 | 108 | Output is logged to the log file regardless of the console logging level. Read and write access to the directory and path of the specified log file are checked to ensure that the directory exists and the script has the necessary permissions to create and modify it. 109 | 110 | ## Integrating with Other Scripts 111 | 112 | You can easily incorporate the Universal OS Detector into your own scripts with the following one-liner: 113 | 114 | ```bash 115 | source <(curl -sL https://raw.githubusercontent.com/mdeacey/universal-os-detector/main/universal-os-detector.sh) && run_detection 116 | ``` 117 | 118 | Alternatively, you can use `wget`: 119 | 120 | ```bash 121 | source <(wget -qO- https://raw.githubusercontent.com/mdeacey/universal-os-detector/main/universal-os-detector.sh) && run_detection 122 | ``` 123 | 124 | This command fetches the script from GitHub, sources it, and executes the `run_detection` function, which performs all detection operations and logs the results. 125 | 126 | ### Example: Using Detection Results in Your Script 127 | 128 | Here's an example of how you can use the detection results in your own script: 129 | 130 | ```bash 131 | #!/bin/bash 132 | 133 | # Source the Universal OS Detector script 134 | source <(curl -sL https://raw.githubusercontent.com/mdeacey/universal-os-detector/main/universal-os-detector.sh) 135 | 136 | # Run the detection 137 | run_detection 138 | 139 | # Use the detection results 140 | if [ "$os" = "Linux" ]; then 141 | echo "This is a Linux system. Running Linux-specific commands..." 142 | # Add your Linux-specific commands here 143 | elif [ "$os" = "MacOS" ]; then 144 | echo "This is a MacOS system. Running MacOS-specific commands..." 145 | # Add your MacOS-specific commands here 146 | elif [ "$os" = "Windows" ]; then 147 | echo "This is a Windows system. Running Windows-specific commands..." 148 | # Add your Windows-specific commands here 149 | else 150 | echo "Unknown operating system: $os" 151 | fi 152 | 153 | # Check for specific distributions 154 | if [ "$os" = "Linux" ] && [[ "$distro_name" == *"Ubuntu"* ]]; then 155 | echo "This is an Ubuntu system. Running Ubuntu-specific commands..." 156 | # Add your Ubuntu-specific commands here 157 | fi 158 | 159 | # Check for containerized environment 160 | if [ "$container" = "true" ]; then 161 | echo "Running inside a container: $container_name" 162 | # Add container-specific commands here 163 | fi 164 | 165 | # Use other detected information 166 | echo "System architecture: $arch" 167 | echo "Kernel version: $kernel" 168 | echo "Desktop environment: $desktop_env" 169 | ``` 170 | 171 | This example shows how you can use the variables set by the Universal OS Detector to make decisions in your script based on the detected system information. 172 | 173 | ## Detected Information 174 | 175 | The script detects and provides information about the following categories. Here's a comprehensive list of all possible values for each, in comma-separated format: 176 | 177 | 1. Container Environment: 178 | Docker, Podman, Kubernetes, LXC, OpenVZ, Generic Container, None 179 | 180 | 2. Operating System: 181 | Linux, Windows, MacOS, FreeBSD, Android, iOS, Solaris, AIX, WSL, Cygwin, MinGW, Unknown 182 | 183 | 3. OS Version/Distribution: 184 | - Linux: Ubuntu xx.xx, Debian xx, CentOS xx, Fedora xx, Red Hat Enterprise Linux xx, Arch Linux x, Manjaro xx, openSUSE xx, Alpine Linux xx, Gentoo x, Slackware x, Linux Mint xx.xx, Elementary OS xx, Kali Linux xx, Parrot OS xx, Deepin xx, Endless OS x, Clear Linux xx, Void Linux x, MX Linux xx, Zorin OS xx, Pop!_OS xx [Other distribution names] 185 | - MacOS: 15.x (Sequoia), 14.x (Sonoma), 13.x (Ventura), 12.x (Monterey), 11.x (Big Sur), 10.15.x (Catalina), 10.14.x (Mojave), 10.13.x (High Sierra), 10.12.x (Sierra), 10.11.x (El Capitan), 10.10.x (Yosemite), [Older versions] 186 | - Windows: Windows 11, Windows 10, Windows 8.1, Windows 8, Windows 7, Windows Server 2022, Windows Server 2019, Windows Server 2016, [Older versions] 187 | - FreeBSD: FreeBSD xx.x, OpenBSD x.x, NetBSD x.x, [Other BSD names and versions] 188 | - Solaris: Oracle Solaris 11.x, Oracle Solaris 10 Update x, [Older versions] 189 | - Android: Android 14 (Upside Down Cake), Android 13 (Tiramisu), Android 12 (Snow Cone), Android 11 (Red Velvet Cake), [Older versions] 190 | - iOS: iOS 17.x, iOS 16.x, iOS 15.x, [Older versions] 191 | - AIX: AIX 7.3, AIX 7.2, AIX 7.1, [Older versions] 192 | 193 | 4. Desktop Environment: 194 | - Linux: GNOME, KDE, Xfce, MATE, Cinnamon, LXDE, LXQt, Pantheon, Enlightenment, Deepin, Budgie, Unity, i3, Awesome, Openbox, Fluxbox, GNOME Flashback, Unknown 195 | - Windows: WSL, Git Bash, Cygwin, PowerShell, Unknown 196 | - MacOS: Aqua 197 | - Android: Android 198 | - iOS: iOS 199 | - AIX: AIX 200 | 201 | 5. Architecture: 202 | x86_64 (64-bit), x86 (32-bit), ARM (32-bit), ARM (64-bit), PowerPC 64-bit (little-endian), RISC-V (64-bit), iOS (ARM), Android (ARM), AIX (PowerPC), SPARC, MIPS, Unknown 203 | 204 | 6. Kernel Version: 205 | Linux [version number], Darwin [version number] (for MacOS and iOS), [version number] (for Windows), FreeBSD [version number], SunOS [version number], [version number]-android[version]-[build number] (for Android), [version number] (for AIX) 206 | 207 | ## Requirements 208 | 209 | - Bash shell 210 | - Standard Unix utilities: uname, tr, grep 211 | 212 | ## Contributing 213 | 214 | Contributions to improve the Universal OS Detector are welcome. Please feel free to submit pull requests or create issues for bugs and feature requests. 215 | 216 | ## License 217 | 218 | MIT License 219 | 220 | Copyright (c) 2024 Marcus Deacey 221 | 222 | Permission is hereby granted, free of charge, to any person obtaining a copy 223 | of this software and associated documentation files (the "Software"), to deal 224 | in the Software without restriction, including without limitation the rights 225 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 226 | copies of the Software, and to permit persons to whom the Software is 227 | furnished to do so, subject to the following conditions: 228 | 229 | The above copyright notice and this permission notice shall be included in all 230 | copies or substantial portions of the Software. 231 | 232 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 233 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 234 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 235 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 236 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 237 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 238 | SOFTWARE. 239 | 240 | ## Contact 241 | 242 | Marcus Deacey 243 | marcusdeacey@gmail.com 244 | 245 | For issues and feature requests, please use the [GitHub Issues page](https://github.com/mdeacey/universal-os-detector/issues). 246 | -------------------------------------------------------------------------------- /universal-os-detector.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | trap 'log "Error encountered at line ${LINENO:-unknown} while executing command: ${BASH_COMMAND:-unknown}" "ERROR"' ERR 5 | trap 'log "Received termination signal. Exiting." "WARN"; cleanup' SIGINT SIGTERM 6 | 7 | #### LOGGING 8 | 9 | log_file=${log_file:-~/universal-os-detector.log} 10 | 11 | console_log_level=${console_log_level:-${CONSOLE_LOG_LEVEL:-1}} 12 | 13 | color_error='\033[0;31m' 14 | color_warn='\033[0;33m' 15 | color_info='\033[0;30m' 16 | color_system='\033[0;37m' 17 | color_reset='\033[0m' 18 | color_debug='\033[0;36m' 19 | 20 | get_log_color() { 21 | local level="$1" 22 | case "$level" in 23 | error) echo "$color_error" ;; 24 | warn) echo "$color_warn" ;; 25 | info) echo "$color_info" ;; 26 | system) echo "$color_system" ;; 27 | debug) echo "$color_debug" ;; 28 | *) echo "$color_reset" ;; 29 | esac 30 | } 31 | 32 | print_log_message() { 33 | local color="$1" 34 | local level="$2" 35 | local message="$3" 36 | local current_time="$4" 37 | 38 | echo -e "${color}$current_time [$level]: $message${COLOR_RESET}" 39 | } 40 | 41 | lowercase() { 42 | echo "$1" | tr '[:upper:]' '[:lower:]' 43 | } 44 | 45 | uppercase() { 46 | echo "$1" | tr '[:lower:]' '[:upper:]' 47 | } 48 | 49 | should_log_to_console() { 50 | local level="$1" 51 | local lower_level 52 | lower_level=$(lowercase "$level") 53 | 54 | case "$console_log_level" in 55 | 0) return 1 ;; 56 | 1) [[ "$lower_level" =~ ^(system|warn|error)$ ]] && return 0 || return 1 ;; 57 | 2) [[ "$lower_level" =~ ^(system|warn|error|info)$ ]] && return 0 || return 1 ;; 58 | 3) return 0 ;; 59 | *) return 1 ;; 60 | esac 61 | } 62 | 63 | log() { 64 | local message="${1:-}" 65 | local level="${2:-info}" 66 | local current_time=$(date '+%Y-%m-%d %H:%M:%S') 67 | 68 | local color 69 | color=$(get_log_color "$level") 70 | 71 | local upper_level 72 | upper_level=$(uppercase "$level") 73 | 74 | echo -e "$current_time [$upper_level]: $message" >> "$log_file" 75 | 76 | if should_log_to_console "$level"; then 77 | print_log_message "$color" "$upper_level" "$message" "$current_time" 78 | fi 79 | } 80 | 81 | #### LOGGING LEVEL VALIDATION 82 | 83 | get_log_level() { 84 | local input="$1" 85 | local lower_case_input=$(lowercase "$input") 86 | 87 | if [[ "$input" =~ ^[0-3]$ ]]; then 88 | echo "$input" 89 | else 90 | case "$lower_case_input" in 91 | none|n) echo 0 ;; 92 | default|d) echo 1 ;; 93 | verbose|v) echo 2 ;; 94 | debug|deb) echo 3 ;; 95 | *) echo -1 ;; 96 | esac 97 | fi 98 | } 99 | 100 | get_text_log_level() { 101 | local level_numeric="$1" 102 | case "$level_numeric" in 103 | 0) echo "NONE" ;; 104 | 1) echo "DEFAULT" ;; 105 | 2) echo "VERBOSE" ;; 106 | 3) echo "DEBUG" ;; 107 | *) echo "UNKNOWN" ;; 108 | esac 109 | } 110 | 111 | handle_invalid_log_level_error() { 112 | local error_message="Error: Invalid console_log_level. Please use one of the following valid options: N/NONE/0, D/DEFAULT/1, V/VERBOSE/2, or DEB/DEBUG/3." 113 | echo -e "${color_error}$error_message${color_reset}" >&2 114 | echo -e "$(date '+%Y-%m-%d %H:%M:%S') [UNKNOWN]: $error_message" >> "$log_file" 115 | exit 1 116 | } 117 | 118 | validate_console_log_level() { 119 | local level_numeric 120 | local level_text 121 | local log_level_message 122 | 123 | level_numeric=$(get_log_level "$console_log_level") 124 | 125 | if [[ "$level_numeric" -eq -1 ]]; then 126 | handle_invalid_log_level_error 127 | fi 128 | 129 | level_text=$(get_text_log_level "$level_numeric") 130 | 131 | console_log_level="$level_numeric" 132 | log_level_message="Console log level is set to: $level_text [$console_log_level]" 133 | log "$log_level_message" info 134 | } 135 | 136 | #### FUNCTIONAL TESTS 137 | 138 | check_command() { 139 | local required_command="$1" 140 | local version_flag="${2:---version}" 141 | local minimum_version="${3:-}" 142 | 143 | if ! command -v "$required_command" &>/dev/null; then 144 | log "Error: Command '$required_command' is not available." error 145 | return 1 146 | fi 147 | 148 | if [ -n "$minimum_version" ]; then 149 | current_version=$($required_command $version_flag 2>&1 | head -n 1 | grep -oE '[0-9]+(\.[0-9]+)+') 150 | if [ "$(printf '%s\n' "$minimum_version" "$current_version" | sort -V | head -n1)" != "$minimum_version" ]; then 151 | log "Error: Command '$required_command' version ($current_version) does not meet minimum version ($minimum_version)." error 152 | return 1 153 | fi 154 | fi 155 | } 156 | 157 | validate_log_file_access() { 158 | log "Verifying READ/WRITE permissions for log file: $log_file..." info 159 | 160 | if [ ! -f "$log_file" ]; then 161 | log "File $log_file does not exist." error 162 | return 1 163 | elif [ ! -r "$log_file" ]; then 164 | log "File $log_file is not readable." error 165 | return 1 166 | elif [ ! -w "$log_file" ]; then 167 | log "File $log_file is not writable." warn 168 | return 1 169 | fi 170 | 171 | return 0 172 | } 173 | 174 | validate_log_dir_access() { 175 | local log_dir=$(dirname "$log_file") 176 | 177 | log "Verifying READ/WRITE permissions for log file directory: $log_dir..." info 178 | 179 | if [ ! -d "$log_dir" ]; then 180 | log "Directory $log_dir does not exist." error 181 | return 1 182 | elif [ ! -r "$log_dir" ]; then 183 | log "Directory $log_dir is not readable." error 184 | return 1 185 | elif [ ! -w "$log_dir" ]; then 186 | log "Directory $log_dir is not writable." error 187 | return 1 188 | fi 189 | 190 | return 0 191 | } 192 | 193 | functional_tests() { 194 | log "Running functional tests..." info 195 | 196 | log "Checking command availability..." info 197 | check_command "uname" || exit 1 198 | check_command "tr" || exit 1 199 | check_command "grep" || exit 1 200 | 201 | validate_log_dir_access || exit 1 202 | validate_log_file_access || exit 1 203 | 204 | log "All functional tests passed." info 205 | } 206 | 207 | #### MAIN DETECTION LOGIC 208 | 209 | #### CONTAINER 210 | 211 | detect_container() { 212 | log "Detecting container environment..." info 213 | 214 | container=false 215 | container_name="None" 216 | 217 | if [ -f /.dockerenv ]; then 218 | container_name="Docker" 219 | elif grep -q 'libpod' /proc/1/cgroup 2>/dev/null; then 220 | container_name="Podman" 221 | elif grep -q '/kubepods' /proc/1/cgroup 2>/dev/null; then 222 | container_name="Kubernetes" 223 | elif grep -q 'lxc' /proc/1/cgroup 2>/dev/null; then 224 | container_name="LXC" 225 | elif grep -q 'VxID' /proc/self/status 2>/dev/null; then 226 | container_name="OpenVZ" 227 | elif grep -q 'docker\|containerd\|lxc' /proc/1/cgroup 2>/dev/null; then 228 | container_name="Generic Container" 229 | fi 230 | 231 | [ "$container_name" != "None" ] && container=true 232 | 233 | log "Container Environment: $container_name" system 234 | } 235 | 236 | #### OS 237 | 238 | detect_os() { 239 | log "Detecting operating system..." info 240 | os_name="" 241 | 242 | uname_str=$(uname -s) 243 | 244 | case "$uname_str" in 245 | Linux) os_name="Linux" ;; 246 | Darwin) os_name="MacOS" ;; 247 | FreeBSD) os_name="FreeBSD" ;; 248 | OpenBSD) os_name="OpenBSD" ;; 249 | NetBSD) os_name="NetBSD" ;; 250 | DragonFlyBSD) os_name="DragonFlyBSD" ;; 251 | SunOS) os_name="Solaris" ;; 252 | AIX) os_name="AIX" ;; 253 | *) log "Quick check did not detect a known OS, running detailed checks..." info ;; 254 | esac 255 | 256 | if [[ -n "$os_name" ]]; then 257 | log "Operating System: $os_name" system 258 | return 259 | fi 260 | 261 | if detect_linux_os || detect_windows_os || detect_macos_os || 262 | detect_freebsd_os || detect_android_os || detect_ios_os || 263 | detect_solaris_os || detect_aix_os || detect_freebsd_os || 264 | detect_openbsd_os || detect_netbsd_os || detect_dragonflybsd_os; then 265 | log "Operating System: $os_name" system 266 | else 267 | log "Unable to detect OS." warn 268 | os_name="Unknown" 269 | log "Operating System: $os_name" system 270 | fi 271 | } 272 | 273 | detect_linux_os() { 274 | if grep -qi "linux" /proc/version 2>/dev/null || 275 | [[ "$(cat /proc/sys/kernel/ostype 2>/dev/null)" == "Linux" ]] || 276 | [[ -d /sys/module ]]; then 277 | os_name="Linux" 278 | return 0 279 | fi 280 | return 1 281 | } 282 | 283 | detect_windows_os() { 284 | if [[ -n "$WINDIR" ]] || 285 | grep -qi microsoft /proc/version 2>/dev/null; then 286 | os_name="Windows" 287 | return 0 288 | elif [[ -f "/proc/version" && $(grep -qi 'wsl' /proc/version) ]]; then 289 | os_name="WSL" 290 | return 0 291 | elif [[ -f "/proc/version" && $(grep -qi 'cygwin' /proc/version) ]]; then 292 | os_name="Cygwin" 293 | return 0 294 | elif [[ -f "/proc/version" && $(grep -qi 'mingw' /proc/version) ]]; then 295 | os_name="MinGW" 296 | return 0 297 | fi 298 | return 1 299 | } 300 | 301 | detect_macos_os() { 302 | if [[ -d /System/Library/CoreServices ]] || 303 | [[ -f /System/Library/CoreServices/SystemVersion.plist ]]; then 304 | os_name="MacOS" 305 | return 0 306 | fi 307 | return 1 308 | } 309 | 310 | detect_android_os() { 311 | if [[ -f "/system/build.prop" || -f "/data/system/packages.xml" ]] || 312 | [[ -d "/system" && -d "/data" ]] || 313 | [[ "$(getprop ro.build.version.release 2>/dev/null)" ]]; then 314 | os_name="Android" 315 | return 0 316 | fi 317 | return 1 318 | } 319 | 320 | detect_ios_os() { 321 | if [[ -d "/var/mobile" ]] || 322 | [[ -f "/System/Library/CoreServices/SystemVersion.plist" ]] || 323 | [[ -f "/usr/bin/ideviceinfo" ]]; then 324 | os_name="iOS" 325 | return 0 326 | fi 327 | return 1 328 | } 329 | 330 | detect_solaris_os() { 331 | if [[ -d "/usr/sbin" && -d "/usr/bin" ]] || 332 | [[ -f "/etc/release" ]]; then 333 | os_name="Solaris" 334 | return 0 335 | fi 336 | return 1 337 | } 338 | 339 | detect_aix_os() { 340 | if [[ -f "/etc/os-release" && $(grep -qi "aix" /etc/os-release) ]] || 341 | [[ -f "/etc/vmlinux" ]]; then 342 | os_name="AIX" 343 | return 0 344 | fi 345 | return 1 346 | } 347 | 348 | detect_freebsd_os() { 349 | if [[ -f /bin/freebsd-version ]] || [[ -d /boot/kernel ]]; then 350 | os_name="FreeBSD" 351 | return 0 352 | fi 353 | return 1 354 | } 355 | 356 | detect_openbsd_os() { 357 | if [[ -f /etc/version ]]; then 358 | os_name="OpenBSD" 359 | return 0 360 | fi 361 | return 1 362 | } 363 | 364 | detect_netbsd_os() { 365 | if [[ -f /etc/netbsd-version ]]; then 366 | os_name="NetBSD" 367 | return 0 368 | fi 369 | return 1 370 | } 371 | 372 | detect_dragonflybsd_os() { 373 | if [[ -f /bin/dfbsd-version ]]; then 374 | os_name="DragonFlyBSD" 375 | return 0 376 | fi 377 | return 1 378 | } 379 | 380 | ### DIST 381 | 382 | detect_linux_dist() { 383 | log "Detecting Linux distribution..." info 384 | if [ -f /etc/os-release ]; then 385 | linux_distro_name=$(grep '^NAME=' /etc/os-release | cut -d '"' -f 2) 386 | elif command -v lsb_release &>/dev/null; then 387 | linux_distro_name=$(lsb_release -si) 388 | else 389 | linux_distro_name="Unknown" 390 | log "Unable to detect Linux Distribution name." warn 391 | fi 392 | log "Linux Distribution Name: $linux_distro_name" system 393 | } 394 | 395 | ### VERSION NUMBER 396 | 397 | detect_version_number() { 398 | log "Detecting version number..." info 399 | 400 | case "$os_name" in 401 | MacOS) detect_macos_ver_no ;; 402 | # Linux) Base on detect_linux_dist TBA ;; 403 | Windows) detect_windows_ver_no ;; 404 | WSL) detect_wsl_ver_no ;; 405 | Cygwin) detect_cygwin_ver_no ;; 406 | MinGW) detect_mingw_ver_no ;; 407 | FreeBSD) detect_freebsd_ver_no ;; 408 | OpenBSD) detect_openbsd_ver_no ;; 409 | NetBSD) detect_netbsd_ver_no ;; 410 | DragonflyBSD) detect_dragonflybsd_ver_no ;; 411 | Solaris) detect_solaris_ver_no ;; 412 | Android) detect_android_ver_no ;; 413 | iOS) detect_ios_ver_no ;; 414 | AIX) detect_aix_ver_no ;; 415 | *) log "Unable to detect version number for $os_name" info ;; 416 | esac 417 | } 418 | 419 | detect_macos_ver_no() { 420 | version_number=$(sw_vers -productVersion 2>/dev/null) 421 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 422 | log "Unable to detect version number." warn 423 | } 424 | 425 | detect_linux_ver_no() { 426 | if [ -f /etc/os-release ]; then 427 | version_number=$(grep -oP '^VERSION="\K[^"]+' /etc/os-release) 428 | elif command -v lsb_release &>/dev/null; then 429 | version_number=$(lsb_release -sr) 430 | elif [ -f /etc/redhat-release ]; then 431 | version_number=$(< /etc/redhat-release) 432 | elif [ -f /etc/debian_version ]; then 433 | version_number="Debian $(< /etc/debian_version)" 434 | fi 435 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 436 | log "Unable to detect version number." warn 437 | } 438 | 439 | detect_windows_ver_no() { 440 | version_number=$(powershell.exe -Command "(Get-WmiObject -Class Win32_OperatingSystem).Version" 2>/dev/null || 441 | wmic os get Version | sed -n 2p || 442 | cmd.exe /c ver) 443 | version_number=$(echo "$version_number" | tr -d '\r') 444 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 445 | log "Unable to detect version number." warn 446 | } 447 | 448 | detect_freebsd_ver_no() { 449 | version_number=$(grep -oP '^VERSION="\K[^"]+' /etc/os-release 2>/dev/null || 450 | sysctl -n kern.version 2>/dev/null || 451 | uname -r) 452 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 453 | log "Unable to detect version number." warn 454 | } 455 | 456 | detect_openbsd_ver_no() { 457 | version_number=$(uname -r || 458 | dmesg | grep -oP 'OpenBSD \K[0-9]+\.[0-9]+' | head -n 1 || 459 | sysctl -n kern.version 2>/dev/null) 460 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 461 | log "Unable to detect version number." warn 462 | } 463 | 464 | detect_netbsd_ver_no() { 465 | version_number=$(uname -r || 466 | sysctl -n kern.version 2>/dev/null || 467 | dmesg | grep -oP 'NetBSD \K[0-9]+\.[0-9]+' | head -n 1) 468 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 469 | log "Unable to detect version number." warn 470 | } 471 | 472 | detect_dragonflybsd_ver_no() { 473 | version_number=$(uname -r || 474 | dmesg | grep -oP 'DragonFly v\K[0-9]+\.[0-9]+' | head -n 1 || 475 | sysctl -n kern.version 2>/dev/null) 476 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 477 | log "Unable to detect version number." warn 478 | } 479 | 480 | detect_wsl_ver_no() { 481 | version_number=$(wsl.exe --version 2>/dev/null | grep -oP 'WSL \K[0-9]+(\.[0-9]+)?' || 482 | uname -r | grep -oP 'Microsoft \K[0-9]+\.[0-9]+' || 483 | powershell.exe -Command "[System.Environment]::OSVersion.Version" 2>/dev/null) 484 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 485 | log "Unable to detect version number." warn 486 | } 487 | 488 | detect_cygwin_ver_no() { 489 | version_number=$(cygcheck -V 2>/dev/null | grep -oP 'Cygwin \K[0-9]+\.[0-9]+' || 490 | uname -r | grep -oP 'cygwin \K[0-9]+\.[0-9]+' || 491 | setup-x86.exe --version 2>/dev/null) 492 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 493 | log "Unable to detect version number." warn 494 | } 495 | 496 | detect_mingw_ver_no() { 497 | version_number=$(gcc --version 2>/dev/null | grep -oP 'gcc \(MinGW\) \K[0-9]+\.[0-9]+' || 498 | mingw-get --version 2>/dev/null | grep -oP 'MinGW \K[0-9]+\.[0-9]+' || 499 | uname -r | grep -oP 'mingw-w64 \K[0-9]+\.[0-9]+') 500 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 501 | log "Unable to detect version number." warn 502 | } 503 | 504 | detect_solaris_ver_no() { 505 | version_number=$(grep -oP '^Oracle Solaris.*:\K.*' /etc/release 2>/dev/null || uname -r) 506 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 507 | log "Unable to detect version number." warn 508 | } 509 | 510 | detect_android_ver_no() { 511 | version_number=$(getprop ro.build.version.release 2>/dev/null) 512 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 513 | log "Unable to detect version number." warn 514 | } 515 | 516 | detect_ios_ver_no() { 517 | version_number=$(ideviceinfo -k ProductVersion 2>/dev/null) 518 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 519 | log "Unable to detect version number." warn 520 | } 521 | 522 | detect_aix_ver_no() { 523 | version_number=$(oslevel 2>/dev/null) 524 | [ -n "$version_number" ] && log "Version Number: $version_number" system || 525 | log "Unable to detect version number." warn 526 | } 527 | 528 | 529 | ### VERSION NAME 530 | 531 | detect_version_name() { 532 | log "Detecting version name..." info 533 | 534 | case "$os_name" in 535 | MacOS) map_macos_ver_name ;; 536 | # Linux) Need to detect Distribution first, fix TBA ;; 537 | Windows) map_win_ver_name ;; 538 | # WSL) TBA ;; 539 | # Cygwin) TBA ;; 540 | # MinGW) TBA ;; 541 | # FreeBSD) TBA ;; 542 | # OpenBSD) TBA ;; 543 | # NetBSD) TBA ;; 544 | # DragonflyBSD) TBA ;; 545 | Solaris) map_solaris_ver_name ;; 546 | Android) map_android_ver_name ;; 547 | # iOS) Numbers only fix TBA ;; 548 | # AIX) Numbers only fix TBA ;; 549 | *) log "No version name available for $os_name" info ;; 550 | esac 551 | } 552 | 553 | map_macos_ver_name() { 554 | case "$version_number" in 555 | 15.*) version_name="Sequoia" ;; 556 | 14.*) version_name="Sonoma" ;; 557 | 13.*) version_name="Ventura" ;; 558 | 12.*) version_name="Monterey" ;; 559 | 11.*) version_name="Big Sur" ;; 560 | 10.15*) version_name="Catalina" ;; 561 | 10.14*) version_name="Mojave" ;; 562 | 10.13*) version_name="High Sierra" ;; 563 | 10.12*) version_name="Sierra" ;; 564 | 10.11*) version_name="El Capitan" ;; 565 | 10.10*) version_name="Yosemite" ;; 566 | *) 567 | version_name="Unknown" 568 | log "Unable to map version number to version name." warn 569 | ;; 570 | esac 571 | log "Version Name: $version_name" system 572 | } 573 | 574 | map_win_ver_name() { 575 | case "$version_number" in 576 | 11.0.*) version_name="Windows 11" ;; 577 | 10.0.*) version_name="Windows 10" ;; 578 | 6.3.*) version_name="Windows 8.1" ;; 579 | 6.2.*) version_name="Windows 8" ;; 580 | 6.1.*) version_name="Windows 7" ;; 581 | 6.0.*) version_name="Windows Vista" ;; 582 | 5.1.*) version_name="Windows XP" ;; 583 | 5.0.*) version_name="Windows 2000" ;; 584 | 4.0.*) version_name="Windows NT 4.0" ;; 585 | 3.51.*) version_name="Windows NT 3.51" ;; 586 | 3.5.*) version_name="Windows NT 3.5" ;; 587 | 3.1.*) version_name="Windows 3.1" ;; 588 | 2.0.*) version_name="Windows 2.0" ;; 589 | 1.0.*) version_name="Windows 1.0" ;; 590 | *) 591 | version_name="Unknown" 592 | log "Unable to map version number to version name." warn 593 | ;; 594 | esac 595 | log "Version Name: $version_name" system 596 | } 597 | 598 | map_solaris_ver_name() { 599 | case "$version_number" in 600 | 11.*) version_name="Oracle Solaris 11" ;; 601 | 10.*) version_name="Oracle Solaris 10" ;; 602 | 5.11) version_name="SunOS 5.11" ;; 603 | 5.10) version_name="SunOS 5.10" ;; 604 | 5.9) version_name="SunOS 5.9" ;; 605 | 5.8) version_name="SunOS 5.8" ;; 606 | *) 607 | version_name="Unknown" 608 | log "Unable to map version number to version name." warn 609 | ;; 610 | esac 611 | log "Version Name: $version_name" system 612 | } 613 | 614 | map_android_ver_name() { 615 | case "$version_number" in 616 | 14.*) version_name="Upside Down Cake" ;; 617 | 13.*) version_name="Tiramisu" ;; 618 | 12.*) version_name="Snow Cone" ;; 619 | 11.*) version_name="Red Velvet Cake" ;; 620 | 10.*) version_name="Quince Tart" ;; 621 | 9.*) version_name="Pie" ;; 622 | 8.*) version_name="Oreo" ;; 623 | 7.*) version_name="Nougat" ;; 624 | 6.*) version_name="Marshmallow" ;; 625 | 5.*) version_name="Lollipop" ;; 626 | 4.4*) version_name="KitKat" ;; 627 | 4.3*) version_name="Jelly Bean" ;; 628 | 4.2*) version_name="Jelly Bean" ;; 629 | 4.1*) version_name="Jelly Bean" ;; 630 | 4.0*) version_name="Ice Cream Sandwich" ;; 631 | 3.*) version_name="Honeycomb" ;; 632 | 2.3*) version_name="Gingerbread" ;; 633 | 2.2*) version_name="FroYo" ;; 634 | 2.1*) version_name="Eclair" ;; 635 | 1.6) version_name="Donut" ;; 636 | 1.5) version_name="Cupcake" ;; 637 | *) 638 | version_name="Unknown" 639 | log "Unable to map version number to version name." warn 640 | ;; 641 | esac 642 | log "Version Name: $version_name" system 643 | } 644 | 645 | 646 | #### DESKTOP ENV 647 | 648 | detect_desktop_env() { 649 | log "Detecting desktop environment..." info 650 | 651 | case "$os_name" in 652 | Linux) 653 | detect_linux_desktop_env 654 | ;; 655 | Windows) 656 | detect_windows_desktop_env 657 | ;; 658 | MacOS|Android|iOS|AIX) 659 | log "Desktop Environment: $os_name" system 660 | ;; 661 | *) 662 | log "Unsupported operating system: $os_name" error 663 | ;; 664 | esac 665 | } 666 | 667 | detect_linux_desktop_env() { 668 | local desktop_env="Unknown Linux desktop environment" 669 | 670 | if [ -n "$xdg_current_desktop" ]; then 671 | desktop_env="$xdg_current_desktop" 672 | elif [ -n "$desktop_session" ]; then 673 | desktop_env="$desktop_session" 674 | elif [ -n "$gdm_session" ]; then 675 | desktop_env="$gdm_session" 676 | else 677 | if command -v kdialog &>/dev/null; then 678 | desktop_env="KDE" 679 | elif command -v gnome-session &>/dev/null; then 680 | desktop_env="GNOME" 681 | elif command -v xfce4-session &>/dev/null; then 682 | desktop_env="Xfce" 683 | elif command -v mate-session &>/dev/null; then 684 | desktop_env="MATE" 685 | elif command -v cinnamon-session &>/dev/null; then 686 | desktop_env="Cinnamon" 687 | elif command -v lxsession &>/dev/null; then 688 | if command -v lxqt-session &>/dev/null; then 689 | desktop_env="LXQt" 690 | else 691 | desktop_env="LXDE" 692 | fi 693 | elif command -v pantheon-session &>/dev/null; then 694 | desktop_env="Pantheon" 695 | elif command -v enlightenment_start &>/dev/null; then 696 | desktop_env="Enlightenment" 697 | elif command -v deepin-session &>/dev/null; then 698 | desktop_env="Deepin" 699 | else 700 | log "No known Linux desktop environment binaries found." warn 701 | fi 702 | fi 703 | log "Linux Desktop Environment: $desktop_env" system 704 | } 705 | 706 | detect_windows_desktop_env() { 707 | local desktop_env="Unknown Windows desktop environment" 708 | 709 | if grep -qi microsoft /proc/version 2>/dev/null; then 710 | desktop_env="WSL (Windows Subsystem for Linux)" 711 | elif [[ "$ostype" == "msys"* ]]; then 712 | desktop_env="Git Bash" 713 | elif [[ "$ostype" == "cygwin"* ]]; then 714 | desktop_env="Cygwin" 715 | elif command -v powershell.exe &>/dev/null; then 716 | desktop_env="PowerShell" 717 | else 718 | desktop_env="Command Prompt (or unknown Windows shell)" 719 | fi 720 | 721 | log "Windows Desktop Environment: $desktop_env" system 722 | } 723 | 724 | #### ARCH 725 | 726 | detect_arch() { 727 | log "Detecting architecture..." info 728 | arch=$(uname -m || echo "Unknown architecture") 729 | 730 | case "$arch" in 731 | x86_64) arch="x86_64 (64-bit)" ;; 732 | i*86) arch="x86 (32-bit)" ;; 733 | armv6l|armv7l) arch="ARM (32-bit)" ;; 734 | aarch64) arch="ARM (64-bit)" ;; 735 | ppc64le) arch="PowerPC 64-bit (little-endian)" ;; 736 | riscv64) arch="RISC-V (64-bit)" ;; 737 | arm*) 738 | if [[ "$OSTYPE" == "darwin"* ]]; then 739 | arch="iOS (ARM)" 740 | else 741 | arch="Android (ARM)" 742 | fi ;; 743 | rs6000) arch="AIX (PowerPC)" ;; 744 | *) arch="Unknown Architecture" 745 | log "Unknown architecture detected: $arch" warn ;; 746 | esac 747 | 748 | log "Architecture: $arch" system 749 | } 750 | 751 | #### KERNEL 752 | 753 | detect_kernel() { 754 | log "Detecting kernel version..." info 755 | 756 | kernel=$(uname -r || echo "Unknown kernel") 757 | 758 | if [[ "$OSTYPE" == "android"* ]]; then 759 | kernel_fallback=$(getprop | grep "ro.build.version.release" | awk -F "=" '{print $2}') 760 | kernel=${kernel:-$kernel_fallback} 761 | elif [[ "$OSTYPE" == "darwin"* ]]; then 762 | kernel_fallback=$(uname -v) 763 | kernel=${kernel:-$kernel_fallback} 764 | fi 765 | 766 | log "Kernel: $kernel" system 767 | } 768 | 769 | #### CLEANUP 770 | 771 | cleanup() { 772 | log "Cleaning up resources..." info 773 | log "Removing log file..." info 774 | rm -f "$log_file".lock 775 | log "Cleanup completed" info 776 | log "Exiting..." info 777 | exit 0 778 | } 779 | 780 | #### MAIN 781 | 782 | run_detection() { 783 | validate_console_log_level 784 | 785 | functional_tests 786 | 787 | log "Starting detection..." INFO 788 | 789 | detect_container 790 | detect_os 791 | 792 | if [[ "$os_name" == "Linux" ]]; then 793 | detect_linux_dist 794 | fi 795 | 796 | detect_version_number 797 | detect_version_name 798 | detect_desktop_env 799 | detect_arch 800 | detect_kernel 801 | 802 | log "Detection completed." INFO 803 | 804 | cleanup 805 | } 806 | 807 | run_detection --------------------------------------------------------------------------------