#!/usr/bin/env bash # ------------------------------------------------------------------------------ # Copyright (c) 2013-2026 Geoffray Levasseur # Protected by the BSD3 license. Please read bellow for details. # # * Redistribution and use in source and binary forms, # * with or without modification, are permitted provided # * that the following conditions are met: # * # * Redistributions of source code must retain the above # * copyright notice, this list of conditions and the # * following disclaimer. # * # * Redistributions in binary form must reproduce the above # * copyright notice, this list of conditions and the following # * disclaimer in the documentation and/or other materials # * provided with the distribution. # * # * Neither the name of the copyright holder nor the names # * of any other contributors may be used to endorse or # * promote products derived from this software without # * specific prior written permission. # * # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR # * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # * OF SUCH DAMAGE. # ------------------------------------------------------------------------------ load_conf info # ------------------------------------------------------------------------------ # Show profile version # Usage: ver ver() { local PARSED PARSED=$(getopt -o h --long help -n 'ver' -- "$@") # shellcheck disable=SC2181 # getopt return code is checked immediately after if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"ver --help\" to display usage." return 1 fi # Restore the getopt-normalised (shell-quoted) argument list eval set -- "$PARSED" while true; do case "$1" in -h|--help) printf "ver: Display the current profile version.\nUsage: ver\n" return 0 ;; --) shift break ;; *) disp E "Invalid options, use \"ver --help\" to display usage." return 1 ;; esac done [[ -z ${PROFVERSION:-} ]] && \ disp W "No version defined. Profile is probably badly installed." && \ return 1 disp "Profile version $PROFVERSION." } export -f ver # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Display weather for the given city (or the default one) # Usage: meteo [city1 city2 ...] meteo() { local PARSED PARSED=$(getopt -o h --long help -n 'meteo' -- "$@") # shellcheck disable=SC2181 # getopt return code is checked immediately after if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"meteo --help\" to display usage." return 1 fi # Restore the getopt-normalised (shell-quoted) argument list eval set -- "$PARSED" while true; do case "$1" in -h|--help) printf "meteo: Fetch weather data.\n" printf "Usage: meteo [city1 city2 ...]\n" printf "If no city is provided, the default city from configuration will be used.\n" return 0 ;; --) shift break ;; *) disp E "Invalid options, use \"meteo --help\" to display usage." return 1 ;; esac done local cities=("$@") local city="" encoded="" [[ $# -eq 0 ]] && cities=("${METEO_DEFAULT_CITY:-}") if [[ ${#cities[@]} -eq 0 || -z "${cities[0]}" ]]; then disp E "No city given and METEO_DEFAULT_CITY is not set. Use 'meteo '." return 1 fi for city in "${cities[@]}"; do encoded=$(urlencode "$city") dwl "https://wttr.in/$encoded" || \ disp E "Failed to fetch weather data for $city." done } export -f meteo # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Display a system information block using only /proc and /sys — no external tools. # Usage: pinfo pinfo() { local _row _row() { printf " %b%-12s%b %b%s%b\n" "$_lbl" "$1" "$_rst" "$_val" "$2" "$_rst" } local PARSED PARSED=$(getopt -o h --long help -n 'pinfo' -- "$@") # shellcheck disable=SC2181 if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"pinfo --help\" to display usage." return 1 fi # Restore the getopt-normalised (shell-quoted) argument list eval set -- "$PARSED" while true; do case "$1" in -h|--help) printf "pinfo: Display system information from /proc and /sys (no external tools required).\n" printf "Usage: pinfo\n" return 0 ;; --) shift break ;; *) disp E "Invalid options, use \"pinfo --help\" to display usage." return 1 ;; esac done # --- Hostname --- local hostname_str if [[ -r /proc/sys/kernel/hostname ]]; then read -r hostname_str < /proc/sys/kernel/hostname else hostname_str="${HOSTNAME:-unknown}" fi # --- OS release (security: parsed line by line, never sourced) --- # /usr/lib/os-release is the documented fallback location. Values may be # wrapped in double or single quotes. local os_name="Unknown" os_version="" _osr_file="" if [[ -r /etc/os-release ]]; then _osr_file=/etc/os-release elif [[ -r /usr/lib/os-release ]]; then _osr_file=/usr/lib/os-release fi if [[ -n $_osr_file ]]; then local _osr_key _osr_val while IFS='=' read -r _osr_key _osr_val; do _osr_val="${_osr_val//\"/}" _osr_val="${_osr_val//\'/}" case "$_osr_key" in NAME) os_name="$_osr_val" ;; VERSION_ID) os_version="$_osr_val" ;; esac done < "$_osr_file" fi # --- Kernel release --- local kernel_str="unknown" [[ -r /proc/sys/kernel/osrelease ]] && read -r kernel_str < /proc/sys/kernel/osrelease # --- Architecture (set by bash at startup from the ELF interpreter) --- local arch_str="${HOSTTYPE:-unknown}" # --- Uptime (seconds from /proc/uptime) --- local uptime_str="unknown" if [[ -r /proc/uptime ]]; then local _upraw read -r _upraw _ < /proc/uptime local _upsec="${_upraw%%.*}" local _days=$(( _upsec / 86400 )) local _hours=$(( (_upsec % 86400) / 3600 )) local _mins=$(( (_upsec % 3600) / 60 )) local _secs=$(( _upsec % 60 )) uptime_str="" (( _days > 0 )) && uptime_str+="${_days}d " (( _hours > 0 || _days > 0 )) && uptime_str+="${_hours}h " uptime_str+="${_mins}m ${_secs}s" fi # --- Load average --- local load_str="unknown" if [[ -r /proc/loadavg ]]; then local _l1 _l5 _l15 read -r _l1 _l5 _l15 _ < /proc/loadavg load_str="${_l1} ${_l5} ${_l15} (1/5/15 min)" fi # --- CPU model and logical/physical core count (pure bash) --- local cpu_model="unknown" cpu_threads=0 local -A _seen_cores=() local _cur_phys="" _cpu_line if [[ -r /proc/cpuinfo ]]; then while IFS= read -r _cpu_line; do case "$_cpu_line" in "model name"*|"Model name"*|"Hardware"*) [[ "$cpu_model" == "unknown" ]] && cpu_model="${_cpu_line#*: }" ;; "processor"*) cpu_threads=$(( cpu_threads + 1 )) ;; "physical id"*) _cur_phys="${_cpu_line#*: }" ;; "core id"*) _seen_cores["${_cur_phys}:${_cpu_line#*: }"]=1 ;; esac done < /proc/cpuinfo fi local cpu_cores="${#_seen_cores[@]}" (( cpu_cores == 0 )) && cpu_cores=$cpu_threads # --- Memory (pure bash, /proc/meminfo, values in kB) --- # MemAvailable only exists since Linux 3.14; on older kernels it is # estimated as free + buffers + cached. local mem_total=0 mem_available=-1 mem_free=0 mem_buffers=0 mem_cached=0 local swap_total=0 swap_free=0 if [[ -r /proc/meminfo ]]; then local _mkey _mval _munit while read -r _mkey _mval _munit; do case "${_mkey%:}" in MemTotal) mem_total="$_mval" ;; MemAvailable) mem_available="$_mval" ;; MemFree) mem_free="$_mval" ;; Buffers) mem_buffers="$_mval" ;; Cached) mem_cached="$_mval" ;; SwapTotal) swap_total="$_mval" ;; SwapFree) swap_free="$_mval" ;; esac done < /proc/meminfo fi (( mem_available < 0 )) && mem_available=$(( mem_free + mem_buffers + mem_cached )) local mem_total_mib=$(( mem_total / 1024 )) local mem_used_mib=$(( (mem_total - mem_available) / 1024 )) local swap_total_mib=$(( swap_total / 1024 )) local swap_used_mib=$(( (swap_total - swap_free) / 1024 )) # --- Process count (glob over numeric /proc entries) --- local proc_count=0 _pdir for _pdir in /proc/[0-9]*/; do [[ -d "$_pdir" ]] && proc_count=$(( proc_count + 1 )) done # --- Shell and terminal --- local shell_str="${BASH:-bash} ${BASH_VERSION%\(*}" local term_str="${TERM:-unknown}" # --- GPU (no external tools required; sysfs first, then /proc/bus/pci) --- local -a gpu_list=() local _gdir _gname _uev_key _uev_val _uev_driver _uev_pci # Preferred: sysfs drm — each card has a device/uevent file readable # without root, giving the PCI id and the kernel driver in use. A label # file (rarely provided by drivers) wins when present. for _gdir in /sys/class/drm/card[0-9]*/device; do [[ -d "$_gdir" ]] || continue # Connector entries (card0-eDP-1, card0-HDMI-A-1, ...) also carry a # "device" link to the same GPU: keep the plain "cardN" entries only. _gname="${_gdir%/device}" [[ "${_gname##*/}" =~ ^card[0-9]+$ ]] || continue _gname="" _uev_driver="" _uev_pci="" if [[ -r "$_gdir/uevent" ]]; then while IFS='=' read -r _uev_key _uev_val; do case "$_uev_key" in DRIVER) _uev_driver="$_uev_val" ;; PCI_ID) _uev_pci="$_uev_val" ;; esac done < "$_gdir/uevent" [[ -n "$_uev_pci" ]] && _gname="PCI ${_uev_pci}" [[ -n "$_uev_driver" ]] && _gname+=" (${_uev_driver})" fi if [[ -r "$_gdir/label" ]]; then read -r _gname < "$_gdir/label" elif [[ -r "$_gdir/../label" ]]; then read -r _gname < "$_gdir/../label" fi [[ -z "$_gname" ]] && _gname="unknown GPU" gpu_list+=("$_gname") done # Fallback: /proc/bus/pci/devices. It holds no human-readable name: the # tab-separated fields are the bus address, vendor+device ids (8 hex # digits), IRQ, the base addresses and sizes, then the kernel driver name # as the last field when one is bound. Only well-known GPU drivers are # reported, as vendor ids alone also match every other device of a vendor. if (( ${#gpu_list[@]} == 0 )) && [[ -r /proc/bus/pci/devices ]]; then local -a _pci=() local _pci_id _pci_drv while IFS=$'\t' read -r -a _pci; do _pci_drv="${_pci[-1]// /}" case "$_pci_drv" in i915|xe|amdgpu|radeon|nouveau|nvidia|virtio-pci|vmwgfx|qxl|bochs-drm|cirrus|mgag200|ast) _pci_id="${_pci[1]}" gpu_list+=("PCI ${_pci_id:0:4}:${_pci_id:4:4} (${_pci_drv})") ;; esac done < /proc/bus/pci/devices fi # --- Render --- # Colours are dropped under NO_COLOR, like everywhere else in the profile. local _lbl="${BIWhite:-}" _val="${ICyan:-}" _rst="${DEFAULTCOL:-}" if [[ -n ${NO_COLOR:-} ]]; then _lbl="" _val="" _rst="" fi printf "\n" _row "Hostname" "$hostname_str" _row "OS" "${os_name}${os_version:+ $os_version}" _row "Kernel" "$kernel_str" _row "Arch" "$arch_str" _row "Uptime" "$uptime_str" _row "Load avg" "$load_str" _row "CPU" "$cpu_model" _row "Cores" "${cpu_cores} physical, ${cpu_threads} logical" if (( ${#gpu_list[@]} > 0 )); then local _gi for _gi in "${!gpu_list[@]}"; do local _gpu_lbl="GPU" (( ${#gpu_list[@]} > 1 )) && _gpu_lbl="GPU $(( _gi + 1 ))" _row "$_gpu_lbl" "${gpu_list[$_gi]}" done fi _row "Memory" "${mem_used_mib} MiB used / ${mem_total_mib} MiB total" if (( swap_total > 0 )); then _row "Swap" "${swap_used_mib} MiB used / ${swap_total_mib} MiB total" fi _row "Processes" "$proc_count" _row "Shell" "$shell_str" _row "Terminal" "$term_str" printf "\n" unset -f _row } export -f pinfo # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Display system general information # Usage: showinfo showinfo() { local PARSED PARSED=$(getopt -o h --long help -n 'showinfo' -- "$@") # shellcheck disable=SC2181 # getopt return code is checked immediately after if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"showinfo --help\" to display usage." return 1 fi # Restore the getopt-normalised (shell-quoted) argument list eval set -- "$PARSED" while true; do case "$1" in -h|--help) printf "showinfo: Display system information (hostname, kernel, uptime and fetch output when available).\n" printf "Usage: showinfo\n" return 0 ;; --) shift break ;; *) disp E "Invalid options, use \"showinfo --help\" to display usage." return 1 ;; esac done local hostname_str figopt=() # /proc first: the hostname binary is missing from minimal installations if [[ -r /proc/sys/kernel/hostname ]]; then read -r hostname_str < /proc/sys/kernel/hostname else hostname_str="${HOSTNAME:-$(hostname 2>/dev/null)}" fi printf "\n" if command -v figlet >/dev/null 2>&1; then [[ -s /usr/share/figlet/ansi_shadow.flf ]] && \ figopt=(-f ansi_shadow) figlet -k "${figopt[@]}" "$hostname_str" else printf "%s\n" "$hostname_str" fi printf "\n" if command -v fastfetch >/dev/null 2>&1; then fastfetch elif command -v neofetch >/dev/null 2>&1; then neofetch else pinfo "$@" fi } export -f showinfo # ------------------------------------------------------------------------------ # EOF