#!/bin/bash # # apt-fast v1.10.0 # Use this just like aptitude or apt-get for faster package downloading. # # Copyright: 2008-2012 Matt Parnell, http://www.mattparnell.com # Improvements, maintenance, revisions - 2012, 2017-2019 Dominique Lasserre # # You may distribute this file under the terms of the GNU General # Public License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # shopt -s nullglob [ -n "$DEBUG" ] && set -xv # Print colored messages. # Usage: msg "message text" "message type" "optional: err" # Message types are 'normal', 'hint' or 'warning'. Warnings and messages with a # third argument are piped to stderr. msg(){ msg_options=() case "$2" in normal) beginColor="$cGreen";; hint) beginColor="$cBlue";; warning) beginColor="$cRed";; question) beginColor="$cRed"; msg_options=(-n);; *) beginColor= ;; esac if [ -z "$3" ] && [ "$2" != "warning" ]; then echo -e "${msg_options[@]}" "${aptfast_prefix}${beginColor}$1${endColor}" else echo -e "${msg_options[@]}" "${aptfast_prefix}${beginColor}$1${endColor}" >&2 fi } # Search for known options and decide if root privileges are needed. root=$# option= for argument in "$@"; do case "$argument" in upgrade | full-upgrade | install | dist-upgrade | build-dep) option="install" ;; clean | autoclean) option="clean" ;; download) option="download" root=0 ;; source) option="source" root=0 ;; changelog | show | help | --help | -h) root=0 ;; esac done # To handle priority of options correctly (environment over config file vars) # we need to preserve all interesting env variables. As this wouldn't be # difficult enough we have to preserve complete env vars (especially if value # ist set (even empty) or not) when changing context (sudo)... # Set a 'random' string to all unset variables. TMP_RANDOM="13979853562951413" TMP_LCK_FILE="${LCK_FILE-${TMP_RANDOM}}" TMP_DOWNLOADBEFORE="${DOWNLOADBEFORE-${TMP_RANDOM}}" TMP__APTMGR="${_APTMGR-${TMP_RANDOM}}" TMP_APTCACHE="${APTCACHE-${TMP_RANDOM}}" TMP_DLDIR="${DLDIR-${TMP_RANDOM}}" TMP_DLLIST="${DLLIST-${TMP_RANDOM}}" TMP__MAXNUM="${MAXNUM-${TMP_RANDOM}}" TMP__MAXCONPERSRV="${MAXCONPERSRV-${TMP_RANDOM}}" TMP__SPLITCON="${SPLITCON-${TMP_RANDOM}}" TMP__MINSPLITSZ=${MINSPLITSZ-${TMP_RANDOM}} TMP__PIECEALGO=${PIECEALGO-${TMP_RANDOM}} TMP_aptfast_prefix="${aptfast_prefix-${TMP_RANDOM}}" TMP_APT_FAST_TIMEOUT="${APT_FAST_TIMEOUT-${TMP_RANDOM}}" TMP_APT_FAST_APT_AUTH="${APT_FAST_APT_AUTH-${TMP_RANDOM}}" TMP_VERBOSE_OUTPUT="${VERBOSE_OUTPUT-${TMP_RANDOM}}" TMP_ftp_proxy="${ftp_proxy-${TMP_RANDOM}}" TMP_http_proxy="${http_proxy-${TMP_RANDOM}}" TMP_https_proxy="${https_proxy-${TMP_RANDOM}}" # Check for proper privileges. # Call explicitly with environment variables to get them into root conext. if [ "$root" -ne 0 ] && [ "$UID" != 0 ]; then exec sudo DEBUG="$DEBUG" \ LCK_FILE="$TMP_LCK_FILE" \ DOWNLOADBEFORE="$TMP_DOWNLOADBEFORE" \ _APTMGR="$TMP__APTMGR" \ APTCACHE="$TMP_APTCACHE" \ DLDIR="$TMP_DLDIR" \ DLLIST="$TMP_DLLIST" \ _MAXNUM="$TMP__MAXNUM" \ _MAXCONPERSRV="$TMP__MAXCONPERSRV" \ _SPLITCON="$TMP__SPLITCON" \ _MINSPLITSZ="$TMP__MINSPLITSZ" \ _PIECEALGO="$TMP__PIECEALGO" \ aptfast_prefix="$TMP_aptfast_prefix" \ APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" \ APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" \ VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" \ ftp_proxy="$TMP_ftp_proxy" \ http_proxy="$TMP_http_proxy" \ https_proxy="$TMP_https_proxy" \ "$0" "$@" fi # Define lockfile. # Use /tmp as directory because everybody (not only root) has to have write # permissions. # We need lock for non-root commands too, because we only have one download # list file. LCK_FILE="/tmp/apt-fast" LCK_FD=99 # Set default package manager, APT cache, temporary download dir, # temporary download list file, and maximal parallel downloads _APTMGR='apt-get' eval "$(apt-config shell APTCACHE Dir::Cache::archives/d)" # Check if APT config option Dir::Cache::archives::apt-fast-partial is set. eval "$(apt-config shell apt_fast_partial Dir::Cache::archives::apt-fast-partial/d)" if [ -z "$apt_fast_partial" ]; then DLDIR="$(realpath "${APTCACHE}/../apt-fast")" else DLDIR="${apt_fast_partial}" fi # Check for apt auth files eval "$(apt-config shell NETRC Dir::Etc::netrc/f)" eval "$(apt-config shell NETRCDIR Dir::Etc::netrcparts/d)" APTAUTHFILES=() if [ -f "$NETRC" ]; then APTAUTHFILES=("$NETRC") fi APTAUTHFILES+=("$NETRCDIR"*) DLLIST="/tmp/apt-fast.list" _MAXNUM=5 _MAXCONPERSRV=10 _SPLITCON=8 _MINSPLITSZ="1M" _PIECEALGO="default" MIRRORS=() # Prefix in front of apt-fast output: aptfast_prefix= # aptfast_prefix="$(date '+%b %_d %T.%N') apt-fast: " # Set color variables. cGreen='\e[0;32m' cRed='\e[0;31m' cBlue='\e[0;34m' endColor='\e[0m' # Set timout value for apt-fast download confirmation dialog. # Value is in seconds. APT_FAST_TIMEOUT=60 # Ask for download confirmation if unset DOWNLOADBEFORE= # Enable APT authentication support APT_FAST_APT_AUTH=1 # Formatted package list in download confirmation if unset VERBOSE_OUTPUT= # Download command. _DOWNLOADER='aria2c --no-conf -c -j ${_MAXNUM} -x ${_MAXCONPERSRV} -s ${_SPLITCON} -i ${DLLIST} --min-split-size=${_MINSPLITSZ} --stream-piece-selector=${_PIECEALGO} --connect-timeout=600 --timeout=600 -m0 --header "Accept: */*"' # Load config file. for Prefix in /usr/local/etc /etc; do CONFFILE=${Prefix}/apt-fast.conf if [ -e "$CONFFILE" ]; then source "$CONFFILE" break fi done # no proxy as default ftp_proxy= http_proxy= https_proxy= # Now overwrite with preserved values if values were set before (compare with # 'random' string). [ "$TMP_LCK_FILE" = "$TMP_RANDOM" ] || LCK_FILE="$TMP_LCK_FILE" [ "$TMP_DOWNLOADBEFORE" = "$TMP_RANDOM" ] || DOWNLOADBEFORE="$TMP_DOWNLOADBEFORE" [ "$TMP__APTMGR" = "$TMP_RANDOM" ] || _APTMGR="$TMP__APTMGR" [ "$TMP_APTCACHE" = "$TMP_RANDOM" ] || APTCACHE="$TMP_APTCACHE" [ "$TMP_DLDIR" = "$TMP_RANDOM" ] || DLDIR="$TMP_DLDIR" [ "$TMP_DLLIST" = "$TMP_RANDOM" ] || DLLIST="$TMP_DLLIST" [ "$TMP__MAXNUM" = "$TMP_RANDOM" ] || _MAXNUM="$TMP__MAXNUM" [ "$TMP__MAXCONPERSRV" = "$TMP_RANDOM" ] || _MAXCONPERSRV="$TMP__MAXCONPERSRV" [ "$TMP__SPLITCON" = "$TMP_RANDOM" ] || _SPLITCON="$TMP__SPLITCON" [ "$TMP__MINSPLITSZ" = "$TMP_RANDOM" ] || _MINSPLITSZ="$TMP__MINSPLITSZ" [ "$TMP__PIECEALGO" = "$TMP_RANDOM" ] || _PIECEALGO="$TMP__PIECEALGO" [ "$TMP_aptfast_prefix" = "$TMP_RANDOM" ] || aptfast_prefix="$TMP_aptfast_prefix" [ "$TMP_APT_FAST_TIMEOUT" = "$TMP_RANDOM" ] || APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" [ "$TMP_APT_FAST_APT_AUTH" = "$TMP_RANDOM" ] || APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" [ "$TMP_VERBOSE_OUTPUT" = "$TMP_RANDOM" ] || VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" [ "$TMP_ftp_proxy" = "$TMP_RANDOM" ] || ftp_proxy="$TMP_ftp_proxy" [ "$TMP_http_proxy" = "$TMP_RANDOM" ] || http_proxy="$TMP_http_proxy" [ "$TMP_https_proxy" = "$TMP_RANDOM" ] || https_proxy="$TMP_https_proxy" # Disable colors if not executed in terminal. if [ ! -t 1 ]; then cGreen= cRed= cBlue= endColor= #FIXME: Time not updated. [ -z "$aptfast_prefix" ] && aptfast_prefix="[apt-fast $(date +"%T")]" fi msg_already_running() { msg "apt-fast already running!" "warning" msg "Verify that all apt-fast processes are finished then remove $LCK_FILE.lock and try again." "hint" } # Check if a lock file exists. if [ -f "$LCK_FILE.lock" ]; then msg_already_running exit 1 fi # create the lock file and lock it, die on failure _create_lock() { eval "exec $LCK_FD>\"$LCK_FILE.lock\"" flock -n $LCK_FD || { msg_already_running; exit 1; } trap "cleanup_aptfast; exit_cleanup_state" EXIT trap "cleanup_aptfast; exit 1" INT TERM } # unlock and remove the lock file _remove_lock() { flock -u "$LCK_FD" 2>/dev/null rm -f "$LCK_FILE.lock" } # Move download file away so missing permissions won't stop usage. CLEANUP_STATE=0 cleanup_dllist() { if [ -f "$DLLIST" ] then if ! mv -- "$DLLIST{,.old}" 2>/dev/null then if ! rm -f -- "$DLLIST" 2>/dev/null then msg "Could not clean up download list file." "warning" CLEANUP_STATE=1 fi fi fi } cleanup_aptfast() { local last_exit_code=$? [ "$CLEANUP_STATE" -eq 0 ] && CLEANUP_STATE=$last_exit_code cleanup_dllist _remove_lock } exit_cleanup_state() { exit $CLEANUP_STATE } # decode url string # translates %xx but must not convert '+' in spaces urldecode() { printf '%b' "${1//%/\\x}" } # Check if mirrors are available. And if so add all mirrors to download list. get_mirrors(){ # Check all mirror lists. for mirrorstr in "${MIRRORS[@]}"; do # Build mirrors array from comma separated string. IFS=", " read -r -a mirrors <<< "$mirrorstr" # Check for all mirrors if URI of $1 is from mirror. If so add all other # mirrors to (resmirror) list and break all loops. for mirror in "${mirrors[@]}"; do # Real expension. if [[ "$1" == "$mirror"* ]]; then filepath="${1#"${mirror}"}" # Build list for aria download list. list="${mirrors[*]}" echo -e "${list// /${filepath}\\t}$filepath\n" return 0 fi done done # No other mirrors found. echo "$1" } AUTH_INFO_PARSED=() # Parse apt authentication files. # Undefined behavior on whitespaces in host, username or password. prepare_auth(){ if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then return fi for auth_file in "${APTAUTHFILES[@]}"; do # auth files have netrc syntax, possible multiline entries starting with "machine" auth_info="$(tr '\n' ' ' < "$auth_file" | sed 's/\(\\)/\n\1/g' | sed '1d')" while IFS= read -r auth; do machine="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" login="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" password="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" # if machine does not have protocol, try https:// if ! [[ "$machine" =~ ^.*:// ]]; then machine="https://$machine" fi if [ -z "$machine" ] || [ -z "$login" ] || [ -z "$password" ]; then msg "Could not parse apt authentication (skipping): $auth ($auth_file)" "warning" continue fi # use space separated string to convert back to array later AUTH_INFO_PARSED+=("$machine $login $password") done <<< "$auth_info" done } # Gets URI as parameter and tries to add basic http credentials. Will fail on # credentials that contain characters that need URL-encoding. get_auth(){ if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then echo "$1" return fi for auth_info in "${AUTH_INFO_PARSED[@]}"; do # convert to array, don't escape variable here auth_info_arr=($auth_info) machine="${auth_info_arr[0]}" # takes first match if [[ "$1" == "$machine"* ]]; then login="${auth_info_arr[1]}" password="${auth_info_arr[2]}" uri="$(echo "$1" | sed "s|^\([^:]\+://\)|\1$login:$password@|")" echo "$uri" return fi done echo "$1" } # Globals to save package name, version, size and overall size. DOWNLOAD_DISPLAY= DOWNLOAD_SIZE=0 # Get the package URLs. get_uris(){ if [ ! -d "$(dirname "$DLLIST")" ] then if ! mkdir -p -- "$(dirname "$DLLIST")" then msg "Could not create download file directory." "warning" CLEANUP_STATE=1 exit fi elif [ -f "$DLLIST" ]; then if ! rm -f -- "$DLLIST" 2>/dev/null && ! touch -- "$DLLIST" 2>/dev/null then msg "Unable to write to download file. Try restarting with root permissions or run 'apt-fast clean' first." "warning" CLEANUP_STATE=1 exit fi fi # Add header to overwrite file. echo "# apt-fast mirror list: $(date)" > "$DLLIST" # NOTE: "aptitude" doesn't have this functionality # so we use "${_APTMGR}" to get package URI's case "$(basename "${_APTMGR}")" in 'apt'|'apt-get') uri_mgr="${_APTMGR}";; *) uri_mgr='apt-get';; esac uris_full="$("$uri_mgr" "${APT_SCRIPT_WARNING[@]}" -y --print-uris "$@")" CLEANUP_STATE="$?" if [ "$CLEANUP_STATE" -ne 0 ] then msg "Package manager quit with exit code." "warning" exit fi prepare_auth ## --print-uris format is: # 'fileurl' filename filesize checksum_hint:filechecksum while IFS=' ' read -r uri filename filesize checksum_string _ do [ -z "$uri" ] && continue uri="$(get_auth "${uri//"'"/}")" IFS=':' read -r hash_algo checksum _ <<<"$checksum_string" filename_decoded="$(urldecode "$filename")" IFS='_' read -r pkg_name_decoded pkg_version_decoded _ <<<"$filename_decoded" DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY}$pkg_name_decoded $pkg_version_decoded" DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY} $(echo "$filesize" | numfmt --to=iec-i --suffix=B)\n" DOWNLOAD_SIZE=$((DOWNLOAD_SIZE + filesize)) ## whole uri comes encoded (urlencoded). Filename must NOT be decoded because # plain aptitude do not decode it when download and install it. Therefore, we # will have ugly named packages at /var/cache/apt/archives but is the standard # behavior. # But package version must be decoded, otherways package=version calls will # not work. if [ -n "$HASH_SUPPORTED" ]; then case "$hash_algo" in SHA512) [ -z "$SHA512_SUPPORTED" ] && hash_algo= || hash_algo=sha-512 ;; SHA256) [ -z "$SHA256_SUPPORTED" ] && hash_algo= || hash_algo=sha-256 ;; SHA1) [ -z "$SHA1_SUPPORTED" ] && hash_algo= || hash_algo=sha-1 ;; MD5Sum) [ -z "$MD5sum_SUPPORTED" ] && hash_algo= || hash_algo=md5 ;; *) hash_algo= esac # Using apt-cache show package=version to ensure recover single and # correct package version. # Warning: assuming that package naming uses '_' as field separator. # Therefore, this code expects package-name_version_arch.deb Otherwise # below code will fail resoundingly if [ -z "$hash_algo" ]; then IFS='_' read -r pkg_name _ <<<"$filename" pkg_version="$pkg_version_decoded" # Transform multi-line field output from apt-cache to single line and sort checksums, strongest first package_info="$(apt-cache show "$pkg_name=$pkg_version" | sed ':r;$!{N;br};s/\n / /g' | sort -r)" while IFS=': ' read -r field checksum _ do case "$field" in SHA512) [ -n "$SHA512_SUPPORTED" ] || continue hash_algo="sha-512" break ;; SHA256) [ -n "$SHA256_SUPPORTED" ] || continue hash_algo="sha-256" break ;; SHA1) [ -n "$SHA1_SUPPORTED" ] || continue hash_algo="sha-1" break ;; MD5sum) [ -n "$MD5sum_SUPPORTED" ] || continue hash_algo="md5" break ;; esac done <<<"$package_info" if [ -z "$hash_algo" ]; then checksum= msg "Couldn't get supported checksum for $pkg_name ($pkg_version)." "warning" REMOVE_WORKING_MESSAGE= fi fi else hash_algo= fi { get_mirrors "$uri" #echo " dir=$DLDIR" if [ -n "$hash_algo" ]; then echo " checksum=$hash_algo=$checksum" fi echo " out=$filename" } >> "$DLLIST" done <<<"$(echo "$uris_full" | grep -E "^'(http(s|)|(s|)ftp)://")" #cat "$DLLIST" #exit } display_downloadfile(){ if [ -n "$VERBOSE_OUTPUT" ]; then cat "$DLLIST" else DISPLAY_SORT_OPTIONS=(-k 1,1) # Sort output after package download size (decreasing): #DISPLAY_SORT_OPTIONS=(-k 3,3 -hr) while IFS=' ' read -r pkg ver size _; do [ -z "$pkg" ] && continue printf '%s%-40s %-20s %10s\n' "$aptfast_prefix" "$pkg" "$ver" "$size" done <<<"$(echo -e "$DOWNLOAD_DISPLAY" | sort "${DISPLAY_SORT_OPTIONS[@]}")" fi msg "Download size: $(echo "$DOWNLOAD_SIZE" | numfmt --to=iec-i --suffix=B)" "normal" } # Create and insert a PID number to lockfile. _create_lock # Make sure aria2c (in general first parameter from _DOWNLOADER) is available. CMD="$(echo "$_DOWNLOADER" | sed 's/^\s*\([^ ]\+\).*$/\1/')" if [ ! "$(command -v "$CMD")" ]; then msg "Command not found: $CMD" "normal" "err" msg "You must configure $CONFFILE to use aria2c or another supported download manager" "normal" "err" CLEANUP_STATE=1 exit fi # Make sure package manager is available. if [ ! "$(command -v "$_APTMGR")" ]; then msg "\`$_APTMGR\` command not available." "warning" msg "You must configure $CONFFILE to use either apt-get or aptitude." "normal" "err" CLEANUP_STATE=1 exit fi # Disable script warning if apt is used. APT_SCRIPT_WARNING=() if [ "$(basename "${_APTMGR}")" == 'apt' ]; then APT_SCRIPT_WARNING=(-o "Apt::Cmd::Disable-Script-Warning=true") fi # Set supported hash algorithms by aria2c (and also by Debian repository). SHA512_SUPPORTED= SHA256_SUPPORTED= SHA1_SUPPORTED= MD5sum_SUPPORTED= HASH_SUPPORTED= if [ "$CMD" == "aria2c" ]; then for supported_hash in $(LC_ALL=C aria2c -v | sed '/^Hash Algorithms:/!d; s/\(^Hash Algorithms: \|,\)\+//g'); do case "$supported_hash" in sha-512) SHA512_SUPPORTED=y; HASH_SUPPORTED=y ;; sha-256) SHA256_SUPPORTED=y; HASH_SUPPORTED=y ;; sha-1) SHA1_SUPPORTED=y; HASH_SUPPORTED=y ;; md5) MD5sum_SUPPORTED=y; HASH_SUPPORTED=y ;; esac done if [ -z "$HASH_SUPPORTED" ]; then msg "Couldn't find supported checksum algorithm from aria2c. Checksums disabled." "warning" fi fi # Check if "assume yes" switch is enabled and if yes enable $DOWNLOADBEFORE. # Also check if "download only" switch is enabled. #TODO: Get real value over APT items APT::Get::Assume-Yes and # APT::Get::Assume-No . # Respectively Aptitude::CmdLine::Download-Only and APT::Get::Download-Only. DOWNLOAD_ONLY= while true; do while getopts ":dy-:" optchar; do case "${optchar}" in -) case "${OPTARG}" in yes | assume-yes) DOWNLOADBEFORE=true ;; assume-no) DOWNLOADBEFORE= ;; download-only) DOWNLOAD_ONLY=true ;; esac ;; y) DOWNLOADBEFORE=true ;; d) DOWNLOAD_ONLY=true ;; *) ;; esac done ((OPTIND++)) [ $OPTIND -gt $# ] && break done # Configure proxies. Use apt values over environment variables. # Note: If proxy setting is not set, there is no apt-config output. # Therefore variable doesn't get overriden, which is intended. # Export the variables to make them available in subshells (aka the # downloader command). eval "$(apt-config shell ftp_proxy Acquire::ftp::proxy)" export ftp_proxy eval "$(apt-config shell http_proxy Acquire::http::proxy)" export http_proxy eval "$(apt-config shell https_proxy Acquire::https::proxy)" export https_proxy # aria2 has no socks support (see https://github.com/aria2/aria2/issues/153) if echo "$http_proxy" | grep -q "^socks5h://" || echo "$https_proxy" | grep -q "^socks5h://"; then msg "Socks proxy detected. Falling back to ${_APTMGR}" "hint" "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" exit fi # Run actions. if [ "$option" == "install" ]; then msg msg "Working... this may take a while." "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 # Test /tmp/apt-fast.list file exists and not just the apt-fast comment line. # Then download all files from the list. if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ] && [ ! "$DOWNLOADBEFORE" ]; then display_downloadfile msg msg "Do you want to download the packages? [Y/n] " "question" while ((!updsys)); do read -r -sn1 -t "$APT_FAST_TIMEOUT" answer || { msg; msg "Timed out." "warning"; CLEANUP_STATE=1; exit; } case "$answer" in [JjYy]) result=1; updsys=1 ;; [Nn]) result=0; updsys=1 ;; "") result=1; updsys=1 ;; *) updsys=0 ;; esac done else result=1 fi if ((DOWNLOAD_SIZE)); then msg # Continue if answer was right or DOWNLOADBEFORE is enabled. if ((result)); then if [ -s "$DLLIST" ]; then # Test if apt-fast directory is present where we put packages. if [ ! -d "$DLDIR" ]; then mkdir -p -- "$DLDIR" fi cd "$DLDIR" &>/dev/null || { msg; msg "Not able to change into download directory." "warning"; CLEANUP_STATE=1; exit; } eval "${_DOWNLOADER}" # execute downloadhelper command if [ "$(find "$DLDIR" -printf . | wc -c)" -gt 1 ]; then # Delete incomplete/corrupted downloaded files, if any: Not recursive, as we don't expect any dirs to exist within $DLDIR. # When Aria2c downloads a file and detects it is corrupted, its filename won't be renamed back to its actual name, # preserving .aria2 file extension, which also indicates when a file hasn't been completely downloaded. for x in *.aria2; do rm -f "$x" "${x%.aria2}" done # Move all packages to the apt install directory by force to ensure # already existing debs which may be incomplete are replaced find . -type f \( -name '*.deb' -o -name '*.ddeb' \) -execdir mv -ft "$APTCACHE" {} \+ fi cd - &>/dev/null || msg "Failed to change back directory" "warning" fi else CLEANUP_STATE=1 exit fi else [ -t 1 ] && tput el fi # different problem resolving for aptitude if [ -z "$DOWNLOAD_ONLY" ] || [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" fi elif [ "$option" == "clean" ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" && { if [ -d "$DLDIR" ]; then find "$DLDIR" -maxdepth 1 -type f -delete CLEANUP_STATE="$?" [ -f "$DLLIST" ] && rm -f -- "$DLLIST"* || true fi } elif [ "$option" == "download" ]; then msg msg "Working... this may take a while." "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ]; then display_downloadfile eval "${_DOWNLOADER}" fi # different problem resolving for aptitude if [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "$@" fi elif [ "$option" == "source" ]; then msg msg "Working... this may take a while." "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ]; then display_downloadfile eval "${_DOWNLOADER}" fi # We use APT manager here to provide more verbose output. This method is # slightly slower then extractiong packages manually after download but also # more hardened (e.g. some options like --compile are available). "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" # Uncomment following snippet to extract source directly and comment # both lines before. #while read srcfile; do # # extract only .dsc files # echo "$srcfile" | grep -q '\.dsc$' || continue # dpkg-source -x "$(basename "$srcfile")" #done < "$DLLIST" # Execute package manager directly if unknown options are passed. else "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" fi # After error or all done remove our lockfile (done with EXIT trap)