#!/bin/bash set -eu USER_AGENT='Termux-PKG/1.0 mirror-checker (termux-tools @PACKAGE_VERSION@) Termux (@TERMUX_APP_PACKAGE@; install-prefix:@TERMUX_PREFIX@)' show_help() { local cache_size cache_size=$(du -sh @TERMUX_CACHE_DIR@/apt/archives 2>/dev/null | cut -f1) echo 'Usage: pkg command [arguments]' echo echo 'A tool for managing packages. Commands:' echo echo ' autoclean - Remove all outdated packages from .deb package' echo ' cache.' echo echo ' clean - Remove all packages from .deb package cache.' [ -n "$cache_size" ] && echo " Using $cache_size now." echo echo ' files - Show all files installed by packages.' echo echo ' install - Install specified packages.' echo echo ' list-all - List all packages available in repositories.' echo echo ' list-installed - List installed packages.' echo echo ' reinstall - Reinstall specified installed packages at the' echo ' latest version.' echo echo ' search - Search package by query, for example by name or' echo ' description part.' echo echo ' show - Show basic metadata, such as dependencies.' echo echo ' uninstall - Uninstall specified packages. Configuration files' echo ' will be left intact.' echo echo ' upgrade - Upgrade all installed packages to the latest' echo ' version.' echo exit 1 } check_mirror() { local mirror="${1%/}" local timeout="${2-5}" timeout "$((timeout + 1))" curl \ --head \ --fail \ --connect-timeout "$timeout" \ --location \ --user-agent "$USER_AGENT" \ "$mirror/dists/stable/Release" >/dev/null 2>&1 } hostname() { echo "$1" | awk -F'[/:]' '{print $4}' } last_modified() { local mtime local now mtime=$(date -r "$1" '+%s') now=$(date '+%s') echo $((now - mtime)) } select_mirror() { local main_repo="https://packages-cf.termux.org/apt/termux-main" declare -A mirrors mirrors[16]="$main_repo" mirrors[14]="https://dl.kcubeterm.com/termux-main" mirrors[13]="https://grimler.se/termux-packages-24" mirrors[12]="https://termux.mentality.rip/termux-main" mirrors[11]="https://termux.astra.in.ua/apt/termux-main" mirrors[10]="https://termux.librehat.com/apt/termux-main" mirrors[ 9]="https://mirror.mwt.me/termux/main" mirrors[ 8]="https://termux.sahilister.in/apt/termux-main" mirrors[ 7]="https://plug-mirror.rcac.purdue.edu/termux/termux-main" local current_mirror current_mirror=$(grep -m 1 -P "^\s*deb\s+" @TERMUX_PREFIX@/etc/apt/sources.list | grep -oP 'https?://[^\s]+') # Do not update mirror if: # * If $TERMUX_PKG_NO_MIRROR_SELECT was set. # * Uses .cn domain - specific to Chinese users. # * Uses mirror.bardia.tech, mirror based in Iran. if [ -n "${TERMUX_PKG_NO_MIRROR_SELECT-}" ] || \ [[ "$(hostname "$current_mirror")" == *".cn" ]] || \ [[ "$(hostname "$current_mirror")" == *"mirror.bardia.tech"* ]]; then return fi # Mirrors are rotated if 6 hours timeout has been passed or mirror is no longer accessible. local pkgcache="@TERMUX_CACHE_DIR@/apt/pkgcache.bin" if [ -e "$pkgcache" ] && (( $(last_modified "$pkgcache") <= 6 * 3600 )); then if [ -n "$current_mirror" ]; then echo -n "Checking availability of current mirror: " if check_mirror "$current_mirror"; then echo "ok" return else echo "bad" fi fi fi # Test mirror availability, remove unaccessible mirrors from list. echo "Testing the available mirrors:" local w total_mirror_weight=0 for w in "${!mirrors[@]}"; do echo -n "[*] ${mirrors[$w]}: " if check_mirror "${mirrors[$w]}"; then echo "ok" total_mirror_weight=$((total_mirror_weight + w)) else echo "bad" unset "mirrors[$w]" fi done unset w # Weight-based mirror selection. local selected_mirror="" if ((total_mirror_weight > 0)); then local w random_weight calc_weight=0 random_weight=$((RANDOM % total_mirror_weight + 1)) for w in $(echo "${!mirrors[@]}" | tr ' ' '\n' | sort -n); do calc_weight=$((calc_weight + w)) if ((calc_weight >= random_weight)); then echo "Picking mirror: ${mirrors[$w]}" selected_mirror="${mirrors[$w]}" break fi done fi if [ -n "$selected_mirror" ]; then echo "deb $selected_mirror/ stable main" > @TERMUX_PREFIX@/etc/apt/sources.list else echo "Using fallback mirror: $main_repo" echo "deb $main_repo/ stable main" > @TERMUX_PREFIX@/etc/apt/sources.list fi } update_apt_cache() { local current_host current_host=$(sed -nE -e 's|^\s*deb\s+https?://(.+)\s+stable\s+main$|\1|p' @TERMUX_PREFIX@/etc/apt/sources.list | head -n 1) if [ -z "$current_host" ]; then # No primary repositories configured? apt update return fi local metadata_file metadata_file=$( list_prefix=$(echo "$current_host" | sed 's|/|_|g') arch=$(dpkg --print-architecture) echo "@TERMUX_PREFIX@/var/lib/apt/lists/${list_prefix}_dists_stable_main_binary-${arch}_Packages" | sed 's|__|_|g' ) if [ ! -e "@TERMUX_CACHE_DIR@/apt/pkgcache.bin" ] || [ ! -e "$metadata_file" ]; then apt update return fi local cache_modified cache_modified=$(last_modified "@TERMUX_CACHE_DIR@/apt/pkgcache.bin") local sources_modified sources_modified=$(last_modified "@TERMUX_PREFIX@/etc/apt/sources.list") if (( sources_modified <= cache_modified )) || (( cache_modified > 1200 )); then apt update fi } if [ $# = 0 ]; then show_help fi CMD="$1" shift 1 case "$CMD" in f*) dpkg -L "$@";; h*) show_help;; sh*|inf*) apt show "$@";; add|i*) select_mirror update_apt_cache apt install "$@" ;; autoc*) apt autoclean;; cl*) apt clean;; list-a*) apt list "$@";; list-i*) apt list --installed "$@";; rei*) apt install --reinstall "$@";; se*) select_mirror update_apt_cache apt search "$@" ;; un*|rem*|rm|del*) apt remove "$@";; up*) select_mirror apt update apt full-upgrade "$@" ;; *) echo "Unknown command: '$CMD' (run 'pkg help' for usage information)"; exit 1;; esac