From ccc5f567c0ca6e1022890dba2627a8fd6b28b12b Mon Sep 17 00:00:00 2001 From: meek2100 <34908880+meek2100@users.noreply.github.com> Date: Fri, 23 May 2025 10:13:57 -0700 Subject: [PATCH] Add support to cache IPK packages for offline install --- opkgscript.sh | 95 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/opkgscript.sh b/opkgscript.sh index d46bbb0..738736f 100755 --- a/opkgscript.sh +++ b/opkgscript.sh @@ -11,21 +11,25 @@ # Thanks, too, to hnyman for important comments on this script # # Version history +# 0.2.3 - added support for selective IPK caching with version retention control for offline recovery installs # 0.2.2 - editorial tweaks to help text -richb-hanvover # 0.2.1 - fixed typo in awk script for dependency detection # 0.2.0 - command interface # 0.1.0 - Initial release -PCKGLIST=/etc/config/opkg.installed # default package list -SCRIPTNAME=$(basename $0) # name of this script -COMMAND="" # command to execute +PCKGLIST=/etc/config/opkg.installed # Default package list +IPK_CACHE_LIST="/etc/config/opkg.ipk_cache_list" # List of packages to cache IPKs for +IPK_CACHE_DIR="/etc/opkg/ipk_cache" # Directory to store cached IPK files +IPK_CACHE_ALL_VERSIONS=false # Cache only the latest IPK per package (default). Set to true to keep all versions. +SCRIPTNAME=$(basename $0) # Name of this script +COMMAND="" # Command to execute -INSTLIST=$(mktemp) # list of packages to install -PREQLIST=$(mktemp) # list of prerequisite packages +INSTLIST=$(mktemp) # List of packages to install +PREQLIST=$(mktemp) # List of prerequisite packages -UPDATE=false # update the package database -OPKGOPT="" # options for opkg calls -VERBOSE=false # be verbose +UPDATE=false # Update the package database +OPKGOPT="" # Options for opkg calls +VERBOSE=false # Be verbose cleanup () { rm -f $INSTLIST $PREQLIST @@ -40,12 +44,18 @@ Available commands: write write a list of currently installed packages install install packages on list not currently installed script output a script to install missing packages + cache-ipks download .ipk files for specific packages to a local cache Options: -u update the package database -t test only, execute opkg commands with --noaction -v be verbose +Configuration: + IPK_CACHE_ALL_VERSIONS in script controls whether to cache all .ipk versions or just the latest. + Default is to cache only the latest. Set IPK_CACHE_ALL_VERSIONS=true to retain all versions. + Warning: caching all versions may consume significant storage and must be cleaned manually. + $SCRIPTNAME can be used to re-install those packages that were installed before a firmware upgrade but are not part of the new firmware image. @@ -70,7 +80,7 @@ Alternatively, you can execute to output a shell script that will contain calls to opkg to install those missing packages. This might be useful if you want to check which packages -would be installed of if you want to edit that list. +would be installed or if you want to edit that list. In order for this script to work after a firmware upgrade or reboot, the opkg database must have been updated. You can use the option -u to do this. @@ -135,6 +145,73 @@ if [ $COMMAND = "write" ] ; then exit 0 fi +# +# Cache IPKs +# + +if [ $COMMAND = "cache-ipks" ] ; then + if [ ! -f "$IPK_CACHE_LIST" ]; then + echo "Error: IPK cache list '$IPK_CACHE_LIST' not found." + echo "Please create this file and list the packages you want to cache (one per line)." + exit 1 + fi + + if [ ! -d "$IPK_CACHE_DIR" ]; then + if $VERBOSE; then + echo "Creating IPK cache directory: $IPK_CACHE_DIR" + fi + mkdir -p "$IPK_CACHE_DIR" || { + echo "Error: Could not create IPK cache directory '$IPK_CACHE_DIR'." + exit 1 + } + fi + + if $VERBOSE; then + echo "Caching IPK files from list: $IPK_CACHE_LIST to $IPK_CACHE_DIR" + fi + + opkg update + + cat "$IPK_CACHE_LIST" | while read PACKAGE; do + PACKAGE=$(echo "$PACKAGE" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + [ -z "$PACKAGE" ] && continue + + IPK_LATEST_FILENAME=$(opkg info "$PACKAGE" | grep "^Filename:" | awk '{print $2}') + [ -z "$IPK_LATEST_FILENAME" ] && { + echo "Warning: Could not find filename for package '$PACKAGE'. Skipping." + continue + } + + BASE_PACKAGE_NAME=$(opkg info "$PACKAGE" | grep "^Package:" | awk '{print $2}') + IPK_LATEST_PATH="$IPK_CACHE_DIR/$IPK_LATEST_FILENAME" + + if ! $IPK_CACHE_ALL_VERSIONS && [ -n "$BASE_PACKAGE_NAME" ]; then + CACHED_FILES=$(find "$IPK_CACHE_DIR" -maxdepth 1 -type f -name "${BASE_PACKAGE_NAME}_*.ipk") + for CACHED_FILE in $CACHED_FILES; do + CACHED_FILENAME=$(basename "$CACHED_FILE") + if [ "$CACHED_FILENAME" != "$IPK_LATEST_FILENAME" ]; then + $VERBOSE && echo "Removing old version of $BASE_PACKAGE_NAME: $CACHED_FILE" + rm -f "$CACHED_FILE" || echo "Warning: Failed to remove $CACHED_FILE" + fi + done + fi + + if [ -f "$IPK_LATEST_PATH" ]; then + $VERBOSE && echo "Package '$PACKAGE' IPK already exists: $IPK_LATEST_FILENAME (skipping download)" + else + $VERBOSE && echo "Downloading '$PACKAGE' (latest version: $IPK_LATEST_FILENAME)" + opkg $OPKGOPT download "$PACKAGE" + if [ $? -eq 0 ] && [ -f "./$IPK_LATEST_FILENAME" ]; then + mv "./$IPK_LATEST_FILENAME" "$IPK_LATEST_PATH" || echo "Error: Failed to move downloaded IPK" + $VERBOSE && echo "Successfully moved to $IPK_CACHE_DIR" + else + echo "Warning: Download failed or file missing for '$PACKAGE'" + fi + fi + done + exit 0 +fi + # # Update #