OpenWrt Forum Archive

Topic: Identify packages to be re-installed after system upgrade

The content of this topic has been archived between 26 Mar 2018 and 23 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

Hello,

I'm looking for an easy way to re-install all packages after a sysupgrade that were installed before the upgrade.

I guess I could save a list of all packages installed before the upgrade. After the upgrade, I would then install all packages on that list that are not already installed. That could be handled by a simple script.

Is it possible to identify the installed packages that were not part of the firmware image but installed later on?

How do you do this? Wouldn't it nice to have a script like that, or a command in LuCI?

Thanks, Malte

opkg list-installed

To save a file which will be preserved over sysupgrade:

opkg list-installed > /etc/config/opkg_installed

This doesn't differentiate between files in the orignal image and those installed later, but you can compare that what you have in the new installation.

(Last edited by lizby on 7 Mar 2013, 19:27)

I have written a small script that can be used to
  * save a list of the currently installed packages before a firmware upgrade, and
  * use that list to reinstall all those packages after a firmware upgrade.
The script takes care not to directly install packages that will be installed automatically in order to fulfill dependencies.

Ideally, a script like this should be installed as part of the firmware image. May be, one the default packages could adopt it?

I'd happily provide my script if there is any interest. Unfortenately, this forum does not seem to support attachments. What is the proper way to make the script available?

You can include the script here inside [ code] and [ /code] tags  (but of course without space after [ that I included to prevent action.)

(Last edited by hnyman on 23 Mar 2013, 13:03)

Ok, here's my script. Comments are welcome.

#! /bin/sh

# Write a list of packages currently installed or read that list,
# presumably after a firmware upgrade, in order to reinstall all packages
# on that list not currently installed
#
# (c) 2013 Malte Forkel <malte.forkel@berlin.de>
#
# Version history
#    0.1.0 - Initial release

PCKGLIST=/etc/config/opkg.installed  # default package list
INSTLIST=$(mktemp)                   # list of packages to install
PREQLIST=$(mktemp)                   # list of prerequisite packages

WRITE=false                          # write a package list
UPDATE=false                         # update the package database
ACTOPT=""                            # test only, no action
VERBOSE=false                        # be verbose

cleanup () {
    rm -f $INSTLIST $PREQLIST
}

echo_usage () {
    echo \
"Usage: $(basename $0) [options] [packagelist]

The default package list is $PCKGLIST.

Options:
  -h     print this help message
  -t     test only, execute opkg commands with --noaction
  -u     update the package database
  -v     be verbose
  -w     write the list of currently installed packages"
}

trap cleanup SIGHUP SIGINT SIGTERM EXIT

# parse command line options
while getopts "htuvw" OPTS; do
    case $OPTS in
        t )
            ACTOPT="--noaction";;
        u )
            UPDATE=true;;
        v )
            VERBOSE=true;;
        w )
            WRITE=true;;
        [h\?*] )
            echo_usage
            exit 0;;
    esac
done
shift $(($OPTIND - 1))

# Set name of the package list
if [ "x$1" != "x" ]; then
    PCKGLIST="$1"
fi

#
# Write
#

if $WRITE; then
    if $VERBOSE; then
        echo "Saving package list to $PCKGLIST"
    fi
    # NOTE: option --noaction not valid for list-installed
    opkg list-installed > "$PCKGLIST"
    exit 0
fi

#
# Update 
#

if $UPDATE; then
    opkg $ACTOPT update
fi

#
# Install
#

# detect uninstalled packages
if $VERBOSE; then
    echo "Checking packages... "
fi
cat "$PCKGLIST" | while read PACKAGE SEP VERSION; do
    # opkg status is much faster than opkg info
    # it only returns status of installed packages
    #if ! opkg status $PACKAGE | grep -q "^Status:.* installed"; then
    if [ "x$(opkg status $PACKAGE)" == "x" ]; then
        # collect uninstalled packages
        echo $PACKAGE >> $INSTLIST
        # collect prerequisites
        opkg info "$PACKAGE" |
        awk "/^Depends: / {
                            sub(\"Depends: \", \"\");   \
                            gsub(\", \", \"\\n\");      \
                            print >> \"$PREQLIST\";      \
                          }"
    fi
done

# install packages
cat "$INSTLIST" | while read PACKAGE; do
    if grep -q "^$PACKAGE\$" "$PREQLIST"; then
        # prerequisite package, will be installed automatically
        if $VERBOSE; then
            echo "$PACKAGE installed automatically"
        fi
    else
        # install package
        opkg $ACTOPT install $PACKAGE
    fi
done

# clean up and exit
exit 0

EDIT: You'll find an improved version further down

(Last edited by mforkel on 23 Mar 2013, 09:50)

Interesting.

I would make the script more foolproof in order to avoid mistakes.
Default action (with no arguments) should be -h = help text.

Currently the install process seems to be the default action, if no argument is given.
Install process should always require an active argument.

The help text might also include explanation of the workflow. If I got it right, you have to "write list" before sysupgrade to the config directory, so that the list survives sysupgrade. After the flash you have to update opkg package list, determine packages to be installed and finally install them.

I would also separate the "determine packages to be installed" and the actual installation step. Most users would probably like to first see the list of packages to be installed, in any case. IT seems to be possible also now, if the -t option is used, but even then it also proceeds to the install itself.


Interesting script in any case. I am publishing a community build here for wndr3700, and might include a polished version of this script to it.

EDIT:
Additional functionality: there might also be an option to echo/print the list of the final opkg install commands. The user might want to remove unneeded packages from the list, and thus having the script to produce a command list might be useful.

(Last edited by hnyman on 10 Mar 2013, 12:56)

Thanks for your suggestions! I think, those are valid points. I tried to adapt my script accordingly.

Feel free to change the script according to your needs. But I would like it even better if we cooperated and came up with one version that we both like.

Here is my new version:

#! /bin/sh

# Write a list of packages currently installed or read that list,
# presumably after a firmware upgrade, in order to reinstall all packages
# on that list not currently installed
#
# (c) 2013 Malte Forkel <malte.forkel@berlin.de>
#
# Version history
#    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

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

cleanup () {
    rm -f $INSTLIST $PREQLIST
}

echo_usage () {
    echo \
"Usage: $(basename $0) [options...] command [packagelist]

Available commands:
    help                print this help text
    write               write a list of currently installed packages
    install             install packages on list not currently installed
    script              output a script to install missing packages
    
Options:
    -u                  update the package database
    -t                  test only, execute opkg commands with --noaction
    -v                  be verbose

$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.

Before the firmware upgrade, execute

    $SCRIPTNAME [options...] write [packagelist]
    
to save the list of currently installed packages. The default package list
is '$PCKGLIST'. Save the package list in a place that will
not be wiped out by the firmware upgrade or copy it to another computer 
before the upgrade.

After the firmware upgrade, execute

    $SCRIPTNAME [options...] install [packagelist]
    
to re-install all packages that were not part of the firmware image.
Alternatively, you can execute

    $SCRIPTNAME [options...] script [packagelist]
    
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.

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.

You can specify the option -t to test what $SCRIPTNAME would do. All calls
to opkg will be made with the option --noaction. This does not influence
the call to opkg to write the list of installed packages, though. 
"
}

trap cleanup SIGHUP SIGINT SIGTERM EXIT

# parse command line options
while getopts "htuvw" OPTS; do
    case $OPTS in
        t )
            OPKGOPT="$OPKGOPT --noaction";;
        u )
            UPDATE=true;;
        v )
            VERBOSE=true;;
        [h\?*] )
            echo_usage
            exit 0;;
    esac
done
shift $(($OPTIND - 1))

# Set the command
COMMAND=$1

# Set name of the package list
if [ "x$2" != "x" ]; then
    PCKGLIST="$2"
fi

#
# Help
#

if [ "x$COMMAND" == "x" ]; then
    echo "No command specified."
    echo ""
    COMMAND="help"
fi

if [ $COMMAND == "help" ]; then
    echo_usage
    exit 0
fi

#
# Write
#

if [ $COMMAND = "write" ] ; then
    if $VERBOSE; then
        echo "Saving package list to $PCKGLIST"
    fi
    # NOTE: option --noaction not valid for list-installed
    opkg list-installed > "$PCKGLIST"
    exit 0
fi

#
# Update 
#

if $UPDATE; then
    opkg $OPKGOPT update
fi

#
# Check
#

if [ $COMMAND == "install" ] || [ $COMMAND == "script" ]; then
    # detect uninstalled packages
    if $VERBOSE && [ $COMMAND != "script" ]; then
        echo "Checking packages... "
    fi
    cat "$PCKGLIST" | while read PACKAGE SEP VERSION; do
        # opkg status is much faster than opkg info
        # it only returns status of installed packages
        #if ! opkg status $PACKAGE | grep -q "^Status:.* installed"; then
        if [ "x$(opkg status $PACKAGE)" == "x" ]; then
            # collect uninstalled packages
            echo $PACKAGE >> $INSTLIST
            # collect prerequisites
            opkg info "$PACKAGE" |
            awk "/^Depends: / {
                                sub(\"Depends: \", \"\");   \
                                gsub(\", \", \"\\n\");      \
                                print >> \"$PREQLIST\";      \
                              }"
        fi
    done
fi

#
# Install or script
#

if [ $COMMAND == "install" ]; then
    # install packages
    cat "$INSTLIST" | while read PACKAGE; do
        if grep -q "^$PACKAGE\$" "$PREQLIST"; then
            # prerequisite package, will be installed automatically
            if $VERBOSE; then
                echo "$PACKAGE installed automatically"
            fi
        else
            # install package
            opkg $OPKGOPT install $PACKAGE
        fi
    done
elif [ $COMMAND == "script" ]; then
    # output install script
    echo "#! /bin/sh"
    cat "$INSTLIST" | while read PACKAGE; do
        if ! grep -q "^$PACKAGE\$" "$PREQLIST"; then
            echo "opkg install $PACKAGE"
        fi
    done
else
    echo "Unknown command '$COMMAND'."
    echo ""
    echo_usage
    exit 1
fi

# clean up and exit
exit 0

EDIT: Fixed awk script, thanks to hnyman

(Last edited by mforkel on 23 Mar 2013, 09:47)

Thanks,
I included that in my newest firmware build and flashed it.

It seems to identify the packages ok, but does not determine dependencies correctly.
I tested with openvpn-openssl and it does not recognize that liblzo is a dependency and wants to install that too.

But otherwise it seems to work.

EDIT:
I am no awk specialist, but I think that your script expects spaces before "Depends:" string, right?
Trunk opkg info does not have spaces, instead the line starts with Depends:

root@OpenWrt:/etc# opkg info openvpn-openssl
Package: openvpn-openssl
Version: 2.3.0-1
Depends: libc, kmod-tun, liblzo, libopenssl
            opkg info "$PACKAGE" |
            awk "/    Depends: / {
                                sub(\"Depends: \", \"\");   \

If I remove the spaces before the first Depends, the dependency detection seems to work.

(Last edited by hnyman on 10 Mar 2013, 21:34)

Sorry for the late reply. Even though I'm subscribed this topic, I didn't receive a notification about your post...

Thanks for pointing out the awk script bug. You're right: The leading spaces are wrong. Actually, that line should read

        awk "/^Depends: / {

I remember doing some editing like 's/^\t/    /', not realizing that OpenWrt's vi does not recognize '^' as a special character in regular expressions. I'm sorry.

Your pattern should work as well.

This is a very nice script :-)

Thanks for the hard work. It shoud go into the Wiki.

I did get a notification for your post, written_direcon smile! Glad you like the script.

Where do you think it would fit into the wiki? To my surprise, I haven't even found a page for backup & restore of the configuration files using LuCI.

Ideally, I think, some functionality like this should be included in the firmware. And made accessible through System > Backup / Flash Firmware > Backup / Restore. Because its probably most useful if its available after you have (re)flashed your device and want to restore your configuration.

I could put the script in a separate package. But I'd rather like to see it included in the existing stuff. Unfortenately, I don't know enough about LuCI to prepare a patch like that. Whom would I have to bribe?

Thanks. After fixing that awk string, the dependency checking seems to work ok.

As editing old messages is possible, I suggest that you correct the awk string in your message with the script, so that the script there is correct and the final version. (You might maybe also remove the first version of the script.)

Good idea! I fixed the script and added a note to the inital version.

(Last edited by mforkel on 23 Mar 2013, 09:51)

I'd remove the scripts in all posts in this thread here and just put the current script into the first post. Makes more sense :-)

Again, I didn't get a notification for you post. :-(

Thanks for your suggestion. I wrote Reinstalling Packages after Firmware Upgrade in Snippets.

(Last edited by mforkel on 28 Mar 2013, 17:37)

Hi,
I wrote a script to automate the process of updating the system (for OpenWrt Attitude Adjustment). With this script updating the firmware with the packages installation it is just simply one command 'install.sh sysupgrade'.

#!/bin/sh
# Install or download packages and/or sysupgrade.
# Script version 1.33 Rafal Drzymala 2013
#
# Changelog
#
#    1.00    RD    First stable code
#    1.04    RD    Change code sequence
#    1.05    RD    Code tune up
#    1.06    RD    Code tune up
#    1.07    RD    ExtRoot code improvements
#    1.08    RD    Add image check sum control
#    1.09    RD    Add command line switch on/off-line package post-install
#                Add command line switch to disable configuration backup 
#    1.10    RD    Preparation scripts code improvements
#    1.11    RD    Preparation scripts code improvements (2)
#    1.12    RD    Preparation scripts code improvements (3)
#    1.13    RD    Preparation scripts code improvements (4)
#    1.14    RD    Extroot scripts code improvements
#    1.15    RD    Help improvements
#    1.16    RD    Help improvements (2), Preparation scripts code improvements (5)
#    1.17    RD    Extroot scripts code improvements (2)
#    1.18    RD    Include installed packages options
#    1.19    RD    Extroot scripts code improvements (3)
#    1.20    RD    Add status led toggle
#    1.21    RD    Correct rc.local manipulation code
#    1.22    RD    Add packages disabling to sysupgrade process
#                Preparation scripts code improvements (5)
#    1.23    RD    Extroot scripts code improvements
#    1.24    RD    Added recurrence of checking of package dependences
#                Changed packages initialization script name convention
#    1.25    RD    Preparation scripts code improvements (6)
#    1.26    RD    Preparation scripts code improvements (7)
#    1.27    RD    Code tune up
#    1.28    RD    Code tune up
#    1.29    RD    Dependency check code improvements
#    1.30    RD    Added post install file removing
#                Added external script
#    1.31    RD    Added backup command
#    1.32    RD    Removed I/O control after post install file removing
#    1.33    RD    Added variables to image source path
#
# Destination /sbin/install.sh
#
. /etc/openwrt_release

local CMD=""
local OFFLINE_POST_INSTALL="1"
local INCLUDE_INSTALLED="1"
local HOST_NAME=""
local BACKUP_ENABLE="1"
local BACKUP_PATH=""
local BACKUP_FILE=""
local INSTALL_PATH="/tmp"
local PACKAGES=""
local IMAGE_SOURCE=""
local IMAGE_FILENAME="sysupgrade.bin"
local POST_INSTALL_SCRIPT="post-installer"
local POST_INSTALLER="/bin/$POST_INSTALL_SCRIPT.sh"
local POST_INSTALLER_LOG="/usr/$POST_INSTALL_SCRIPT.log"
local INSTALLER_KEEP_FILE="/lib/upgrade/keep.d/$POST_INSTALL_SCRIPT"
local RC_LOCAL="/etc/rc.local"
local POST_INSTALL_REMOVE="/etc/config/*-opkg"
local RUN_SCRIPT=""

check_exit_code() {
    local CODE=$?
    if [ $CODE != 0 ]; then 
        echo "Abort, error ($CODE) detected!"
        exit $CODE
    fi
}

get_mount_device() { # <Path to check>
    local CHECK_PATH=$1
    [ -L $CHECK_PATH ] && CHECK_PATH=$($BIN_LS -l $CHECK_PATH | $BIN_AWK -F " -> " '{print $2}')
    $BIN_AWK -v path="$CHECK_PATH" 'BEGIN{FS=" ";device=""}path~"^"$2{if($2>point){device=$1;point=$2}}END{print device}' /proc/mounts
    check_exit_code
}

which_binary() { # <Name of Binary> [<Name of Binary> [...]]
    while [ -n "$1" ]; do
        local WHICH=$(which $1)
        if [ "$WHICH" == "" ]; then
            echo "Binary $1 not found in system!"
            exit 1
        else
            eval "export -- \"BIN_$(echo $1 | tr '[a-z]' '[A-Z]')=$WHICH\""
        fi
        shift
    done
}

add_to_keep_file() { # <Content to save> <Root path>
    local CONTENT="$1"
    local ROOT_PATH="$2"
    $BIN_ECHO "$1">>$ROOT_PATH$INSTALLER_KEEP_FILE
    check_exit_code
}

run_script() { # <Event>
    if [ "$RUN_SCRIPT" != "" ] && [ -x $RUN_SCRIPT ]; then
        $BIN_ECHO "Run script $RUN_SCRIPT $1 ..."
        $RUN_SCRIPT $1
        check_exit_code
        $BIN_ECHO "Script $RUN_SCRIPT exited."
    fi
}

add_to_post_installer_log() { # <Content to save>
    $BIN_ECHO "$($BIN_DATE) $1">>$POST_INSTALLER_LOG
}

package_script_execute() { # <Package> <Script name> <Command>
    local PACKAGE="$1"
    local SCRIPT="$2"
    local CMD="$3"
    if [ -x $SCRIPT ]; then
        $BIN_ECHO "Executing $SCRIPT $CMD for package $PACKAGE"
        if [ "$CMD" == "enable" ] || [ "$CMD" == "stop" ]; then
            $SCRIPT $CMD
        else
            $SCRIPT $CMD
            check_exit_code
        fi
    fi
}

update_path_vars() { # <String to update>
    local PATH_VARS="$1"
    local TARGET=$(echo "$DISTRIB_TARGET" | cut -d "/" -f 1)
    local SUBTARGET=$(echo "$DISTRIB_TARGET" | cut -d "/" -f 2)
    local BOARD_NAME=$($BIN_CAT /tmp/sysinfo/model | $BIN_TR '[A-Z]' '[a-z]')
    local BOARD_VER=$($BIN_ECHO "$BOARD_NAME" | $BIN_CUT -d " " -f 3)
    BOARD_NAME=$($BIN_ECHO "$BOARD_NAME" | $BIN_CUT -d " " -f 2)
    [ -n "$BOARD_VER" ] && BOARD_NAME="$BOARD_NAME-$BOARD_VER"
    [ -n "$DISTRIB_CODENAME" ] && PATH_VARS=${PATH_VARS//\<CODENAME\>/$DISTRIB_CODENAME}
    [ -n "$TARGET" ] && PATH_VARS=${PATH_VARS//\<TARGET\>/$TARGET}
    [ -n "$SUBTARGET" ] && PATH_VARS=${PATH_VARS//\<SUBTARGET\>/$SUBTARGET}
    [ -n "$BOARD_NAME" ] && PATH_VARS=${PATH_VARS//\<HARDWARE\>/$BOARD_NAME}
    $BIN_ECHO "$PATH_VARS"
}

caution_alert() {
    local KEY
    $BIN_ECHO "Caution!"
    $BIN_ECHO "You can damage the system or hardware. You perform this operation at your own risk."
    read -t 60 -n 1 -p "Press Y to continue " KEY
    $BIN_ECHO ""
    [ "$KEY" != "Y" ] && exit 0
}

print_help() {
    $BIN_ECHO -e "Usage:"\
            "\n\t$0 [install|download|sysupgrade] [-h|--help] [-o|--online] [-b|--backup-off] [-i|--exclude-installed]"\
            "\n\nCommands:"\
            "\n\t\tdownload\tdownload all packages and system image do install directory,"\
            "\n\t\tinstall\t\tbackup configuration,"\
            "\n\t\t\t\tstop and disable packages,"\
            "\n\t\t\t\tinstall packages,"\
            "\n\t\t\t\trestore configuration,"\
            "\n\t\t\t\tenable and start packages."\
            "\n\t\tsysupgrade\tbackup configuration,"\
            "\n\t\t\t\tdownload all packages and system image do install directory (in off-line mode),"\
            "\n\t\t\t\tprepare post upgrade package installer,"\
            "\n\t\t\t\tsystem upgrade,"\
            "\n\t\t\t\t... reboot system ...,"\
            "\n\t\t\t\tif extroot exist, clean check sum and reboot system,"\
            "\n\t\t\t\tinstall packages,"\
            "\n\t\t\t\trestore configuration,"\
            "\n\t\t\t\tcleanup installation,"\
            "\n\t\t\t\t... reboot system ..."\
            "\n\t\tbackup\t\tbackup configuration"\
            "\n\nOptions:"\
            "\n\t\t-h\t\tThis help,"\
            "\n\t\t-b\t\tDisable configuration backup and restore during installation or system upgrade process."\
            "\n\t\t\t\tBy default, backup and restore configuration are enabled."\
            "\n\t\t\t\tPath to backup have to on external device otherwise during system upgrade can be lost."\
            "\n\t\t-o\t\tOnline packages installation by post-installer."\
            "\n\t\t\t\tInternet connection is needed after system restart and before packages installation."\
            "\n\t\t-i\t\tExclude installed packages. Only packages from configuration can be processed."\
            "\n\nCurrent configuration:"\
            "\n\tLocal install directory : '$($BIN_UCI -q get system.@sysupgrade[0].localinstall)'"\
            "\n\tConfiguration backup direcory : '$($BIN_UCI -q get system.@sysupgrade[0].backupconfig)'"\
            "\n\tImage source URL : '$($BIN_UCI -q get system.@sysupgrade[0].imagesource)'"\
            "\n\tRun external script : '$($BIN_UCI -q get system.@sysupgrade[0].runscript)'"\
            "\n\tPackages: '$($BIN_UCI -q get system.@sysupgrade[0].opkg)'"\
            "\n\nExamples configuration in /etc/config/system"\
            "\n\tconfig sysupgrade"\
            "\n\t\toption localinstall '/install'"\
            "\n\t\toption backupconfig '/backup'"\
            "\n\t\toption imagesource 'http://ecco.selfip.net/<CODENAME>/<TARGET>/openwrt-<TARGET>-<SUBTARGET>-<HARDWARE>-squashfs-sysupgrade.bin'"\
            "\n\t\tlist opkg libusb"\
            "\n\t\tlist opkg kmod-usb-serial-option"\
            "\n\t\tlist opkg kmod-usb-net-cdc-ether"\
            "\n"
    exit 0
}

initialize() { # <Script parametrs>
    which_binary echo basename dirname logger chmod uci date ls cat cut tr wc rm mv sync reboot awk grep wget opkg sysupgrade md5sum ping logread gzip
    while [ -n "$1" ]; do
        case "$1" in
            install|download|sysupgrade|backup) CMD="$1";; 
            -h|--help) print_help;;
            -b|--backup-off) BACKUP_ENABLE="";;
            -o|--online) OFFLINE_POST_INSTALL="";;
            -i|--exclude-installed) INCLUDE_INSTALLED="";;
            -*) $BIN_ECHO "Invalid option: $1";print_help;;
            *) $BIN_ECHO "Invalid command: $1";print_help;;
        esac
        shift
    done
    [ "$CMD" == "" ] && CMD=install
    [ "$CMD" == "backup" ] && BACKUP_ENABLE="1"
    HOST_NAME=$($BIN_UCI -q get system.@system[0].hostname)
    if [ "$HOST_NAME" == "" ]; then 
        $BIN_ECHO "Error while getting host name!"
        exit 1
    fi
    if [ "$CMD" == "download" ] || ([ "$CMD" == "sysupgrade" ] && [ "$OFFLINE_POST_INSTALL" != "" ]); then
        INSTALL_PATH=$($BIN_UCI -q get system.@sysupgrade[0].localinstall)
        if [ "$INSTALL_PATH" == "" ]; then
            $BIN_ECHO "Install path is empty!"
            exit 1
        fi    
        if [ ! -d "$INSTALL_PATH" ]; then
            $BIN_ECHO "Install path not exist!"
            exit 1
        fi    
    fi
    if [ "$BACKUP_ENABLE" != "" ]; then
        BACKUP_PATH=$($BIN_UCI -q get system.@sysupgrade[0].backupconfig)
        BACKUP_FILE="$BACKUP_PATH/backup-$HOST_NAME-$($BIN_DATE +%Y-%m-%d-%H-%M-%S).tar.gz"        
        if [ ! -d "$BACKUP_PATH" ]; then
            $BIN_ECHO "Backup path not exist!"
            exit 1
        fi
        local MOUNT_DEVICE=$(get_mount_device $BACKUP_PATH)
        if [ "$MOUNT_DEVICE" == "rootfs" ] || [ "$MOUNT_DEVICE" == "sysfs" ] || [ "$MOUNT_DEVICE" == "tmpfs" ]; then
            $BIN_ECHO "Backup path ($BACKUP_PATH) must be on external device. Now is mounted on $MOUNT_DEVICE."
            exit 1
        fi
    fi
    if [ "$CMD" == "download" ] || [ "$CMD" == "sysupgrade" ]; then
        IMAGE_SOURCE=$($BIN_UCI -q get system.@sysupgrade[0].imagesource)
        local IMAGE_PREFIX=$($BIN_UCI -q get system.@sysupgrade[0].imageprefix)
        local IMAGE_SUFFIX=$($BIN_UCI -q get system.@sysupgrade[0].imagesuffix)
        if [ -n "$IMAGE_PREFIX" ] || [ -n "$IMAGE_SUFFIX" ]; then
            IMAGE_SOURCE="$IMAGE_SOURCE/$IMAGE_PREFIX<HARDWARE>$IMAGE_SUFFIX"
        fi
    fi
    RUN_SCRIPT=$($BIN_UCI -q get system.@sysupgrade[0].runscript)
    PACKAGES=$($BIN_UCI -q get system.@sysupgrade[0].opkg)
    if [ "$CMD" == "sysupgrade" ] && [ "$OFFLINE_POST_INSTALL" != "" ]; then
        local MOUNT_DEVICE=$(get_mount_device $INSTALL_PATH)
        if [ "$MOUNT_DEVICE" == "rootfs" ] || [ "$MOUNT_DEVICE" == "sysfs" ] || [ "$MOUNT_DEVICE" == "tmpfs" ]; then
            $BIN_ECHO "Install path ($INSTALL_PATH) must be on external device. Now is mounted on $MOUNT_DEVICE."
            exit 1
        fi
    fi
    $BIN_ECHO "Operation $CMD on $HOST_NAME - $DISTRIB_ID $DISTRIB_RELEASE ($DISTRIB_REVISION)"
}

update_repository() {
    run_script before_opkg_update
    $BIN_ECHO "Updating packages repository ..."
    $BIN_OPKG update
    check_exit_code
    $BIN_ECHO "Packages repository updated."
}

check_installed() {
    if [ "$INCLUDE_INSTALLED" != "" ]; then
        $BIN_ECHO "Checking installed packages ..."
        local INSTALLED=$($BIN_AWK -v PKG="$PACKAGES " 'BEGIN{FS=": ";ORS=" "}/^Package\: /{Package=$2}/^Status\: / && /user installed/{if(index(PKG,Package" ")==0)print Package}' /usr/lib/opkg/status)
        check_exit_code
        INSTALLED=${INSTALLED%% }
        if [ "$INSTALLED" != "" ]; then
            $BIN_ECHO "Installed packages not in configuration: $INSTALLED."
            PACKAGES="$PACKAGES $INSTALLED"
        else
            $BIN_ECHO "All packages from configuration."
        fi
    fi
}

check_dependency() {
    if [ "$PACKAGES" != "" ]; then 
        $BIN_ECHO "Checking packages dependency ..."
        $BIN_ECHO "Main packages: $PACKAGES."
        local PACKAGES_COUNT=-1
        while [ "$($BIN_ECHO $PACKAGES | $BIN_WC -w)" != "$PACKAGES_COUNT" ]; do
            PACKAGES_COUNT=$($BIN_ECHO $PACKAGES | $BIN_WC -w)
            local DEPENDS
            local DEPENDS_COUNT=-1
            while [ "$($BIN_ECHO $DEPENDS | $BIN_WC -w)" != "$DEPENDS_COUNT" ]; do
                DEPENDS_COUNT=$($BIN_ECHO $DEPENDS | $BIN_WC -w)
                DEPENDS=$DEPENDS$($BIN_OPKG depends -A $DEPENDS $PACKAGES | $BIN_AWK -v PKG="$DEPENDS $PACKAGES " 'BEGIN{ORS=" "}{if($2=="" && !seen[$1]++ && index(PKG,$1" ")==0)print $1}')
                check_exit_code
            done
            DEPENDS=${DEPENDS%% }
            [ "$DEPENDS" != "" ] && PACKAGES="$DEPENDS $PACKAGES"
            PACKAGES=$($BIN_OPKG whatprovides -A $PACKAGES | $BIN_AWK -v PKG="$PACKAGES " 'function Select(){if(CNT<1)return;SEL=0;for(ITEM in LIST)if(index(PKG,LIST[ITEM]" ")!=0)SEL=ITEM;if(!seen[LIST[SEL]]++)print LIST[SEL];delete LIST;CNT=0}BEGIN{ORS=" "}{if($3!="")Select();else LIST[CNT++]=$1}END{Select()}')
            PACKAGES=${PACKAGES%% }
        done
        $BIN_ECHO "All packages: $PACKAGES."
    fi
}

config_backup() {
    if [ "$BACKUP_ENABLE" != "" ]; then
        if [ ! -d "$BACKUP_PATH" ]; then
            $BIN_ECHO "Backup path not exist."
            exit 1
        fi
        if [ "$BACKUP_FILE" == "" ]; then
            $BIN_ECHO "Backup file name is empty."
            exit 1
        fi
        $BIN_ECHO "Making configuration backup to $BACKUP_FILE ..."
        $BIN_SYSUPGRADE --create-backup $BACKUP_FILE
        check_exit_code
        $BIN_CHMOD 640 $BACKUP_FILE
        check_exit_code
        $BIN_ECHO "Configuration backuped."
    fi
}

config_restore() {
    if [ "$BACKUP_ENABLE" != "" ]; then
        if [ "$BACKUP_FILE" == "" ]; then
            $BIN_ECHO "Backup file name is empty."
            exit 1
        else
            $BIN_ECHO "Restoring configuration from backup $BACKUP_FILE ..."
            $BIN_SYSUPGRADE --restore-backup $BACKUP_FILE
            check_exit_code
            $BIN_ECHO "Configuration restored."
        fi
    fi
}

packages_disable() {
    if [ "$PACKAGES" != "" ]; then 
        $BIN_ECHO "Disabling packages ..."
        local SCRIPT
        for PACKAGE in $PACKAGES; do
            for SCRIPT in $($BIN_OPKG files $PACKAGE | $BIN_GREP /etc/init.d/); do
                package_script_execute $PACKAGE $SCRIPT disable
                package_script_execute $PACKAGE $SCRIPT stop
            done
        done
        $BIN_ECHO "Packages are disabled."
    fi
}

packages_enable() {
    if [ "$PACKAGES" != "" ]; then 
        $BIN_ECHO "Enabling packages ..."
        local SCRIPT
        for PACKAGE in $PACKAGES; do
            for SCRIPT in $($BIN_OPKG files $PACKAGE | $BIN_GREP /etc/init.d/); do
                package_script_execute $PACKAGE $SCRIPT enable
                package_script_execute $PACKAGE $SCRIPT start
            done
        done
        $BIN_ECHO "Packages are enabled."
    fi
}

packages_install() {
    if [ "$PACKAGES" != "" ]; then 
        run_script before_opkg_install
        $BIN_ECHO "Installing packages ..."
        $BIN_OPKG $CMD $PACKAGES
        check_exit_code
        $BIN_RM $POST_INSTALL_REMOVE
        $BIN_ECHO "Packages are installed."
        run_script after_opkg_install
    fi
}

packages_download() {
    if [ "$PACKAGES" != "" ]; then 
        local PACKAGES_FILE="Packages"
        local PACKAGES_LIST="$PACKAGES_FILE.gz"
        $BIN_ECHO "Downloading packages to $INSTALL_PATH ..."
        cd $INSTALL_PATH
        $BIN_RM -f *.ipk
        $BIN_OPKG download $PACKAGES
        check_exit_code
        $BIN_ECHO "Building packages information ..."
        [ -f $INSTALL_PATH/$PACKAGES_FILE ] && $BIN_RM -f $INSTALL_PATH/$PACKAGES_FILE
        [ -f $INSTALL_PATH/$PACKAGES_LIST ] && $BIN_RM -f $INSTALL_PATH/$PACKAGES_LIST
        for PACKAGE in $PACKAGES; do
            $BIN_ECHO "Getting information for package $PACKAGE."
            $BIN_OPKG info $PACKAGE >>$INSTALL_PATH/$PACKAGES_FILE
            check_exit_code
        done 
        $BIN_ECHO "Compressing packages information as $INSTALL_PATH/$PACKAGES_LIST ..."
        $BIN_AWK '{if($0!~/^Status\:|^Installed-Time\:/)print $0}' $INSTALL_PATH/$PACKAGES_FILE | $BIN_GZIP -c9 >$INSTALL_PATH/$PACKAGES_LIST
        check_exit_code
        $BIN_RM -f $INSTALL_PATH/$PACKAGES_FILE
        check_exit_code
        $BIN_ECHO "Packages are downloaded."
    fi
}

image_download() {
    if [ "$IMAGE_SOURCE" == "" ]; then 
        $BIN_ECHO "Image source information is empty."
        exit 1
    fi
    local IMAGE_REMOTE_NAME="$(update_path_vars $IMAGE_SOURCE)"
    local IMAGE_LOCAL_NAME="$INSTALL_PATH/$IMAGE_FILENAME"
    local SUMS_REMOTE_NAME="$($BIN_DIRNAME $IMAGE_REMOTE_NAME)/md5sums"
    local SUMS_LOCAL_NAME="$INSTALL_PATH/md5sums"
    [ -f $IMAGE_LOCAL_NAME ] && $BIN_RM -f $IMAGE_LOCAL_NAME
    $BIN_ECHO "Downloading system image as $IMAGE_LOCAL_NAME from $IMAGE_REMOTE_NAME ..."    
    $BIN_WGET -O $IMAGE_LOCAL_NAME $IMAGE_REMOTE_NAME
    check_exit_code
    $BIN_ECHO "Downloading images sums as $SUMS_LOCAL_NAME from $SUMS_REMOTE_NAME ..."    
    $BIN_WGET -O $SUMS_LOCAL_NAME $SUMS_REMOTE_NAME
    check_exit_code
    $BIN_ECHO "Checking system image control sum ..."    
    local SUM_ORG=$($BIN_GREP $($BIN_BASENAME $IMAGE_REMOTE_NAME) $SUMS_LOCAL_NAME | $BIN_CUT -d " " -f 1)
    check_exit_code
    local SUM_FILE=$($BIN_MD5SUM $IMAGE_LOCAL_NAME | $BIN_CUT -d " " -f 1)
    check_exit_code
    if [ "$SUM_ORG" == "" ]; then
        $BIN_ECHO "Can't get original control sum!"
        exit 1
    elif [ "$SUM_FILE" == "" ]; then
        $BIN_ECHO "Can't calculate system image control sum!"
        exit 1
    elif [ "$SUM_ORG" != "$SUM_FILE" ]; then
        $BIN_ECHO "Downloaded system image is damaged!"
        exit 1
    else
        $BIN_ECHO "System image is downloaded and checksum is correct."
    fi
    run_script after_image_downloaded
}

installer_prepare() {
    $BIN_ECHO "Preparing packages installer in $POST_INSTALLER ..."
    $BIN_ECHO -e "#!/bin/sh"\
            "\n# Script auto-generated by $0"\
            "\n. /etc/diag.sh"\
            "\nget_status_led"\
            "\nset_state preinit"\
            "\nif [ -d /tmp/overlay-disabled ]; then"\
            "\n\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Removing overlay-rootfs checksum and force reboot\""\
            "\n\t$BIN_RM -f /tmp/overlay-disabled/.extroot.md5sum"\
            "\n\t$BIN_RM -f /tmp/overlay-disabled/etc/extroot.md5sum"\
            "\nelif [ -d /tmp/whole_root-disabled ]; then"\
            "\n\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Removing whole-rootfs checksum and force reboot\""\
            "\n\t$BIN_RM -f /tmp/whole_root-disabled/.extroot.md5sum"\
            "\n\t$BIN_RM -f /tmp/whole_root-disabled/etc/extroot.md5sum"\
            "\nelse"\
            "\n\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Start instalation of packages\"">$POST_INSTALLER
    check_exit_code
    if [ "$OFFLINE_POST_INSTALL" != "" ]; then
        $BIN_ECHO -e "\t$BIN_CAT /etc/opkg.conf | $BIN_AWK 'BEGIN{print \"src/gz local file:/$INSTALL_PATH\"}!/^src/{print \$0}' >/etc/opkg.conf">>$POST_INSTALLER
        check_exit_code
    else
        $BIN_ECHO -e "\tuntil $BIN_PING -q -W 30 -c 1 8.8.8.8 &>/dev/null; do"\
                "\t\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Wait for internet connection\""\
                "\n\tdone">>$POST_INSTALLER
        check_exit_code
    fi
    if [ "$RUN_SCRIPT" != "" ] && [ -x $RUN_SCRIPT ]; then
        $BIN_ECHO -e "\t$RUN_SCRIPT before_opkg_update | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT">>$POST_INSTALLER
        check_exit_code
    fi
    $BIN_ECHO -e "\t$BIN_OPKG update | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT">>$POST_INSTALLER
    if [ "$RUN_SCRIPT" != "" ] && [ -x $RUN_SCRIPT ]; then
        $BIN_ECHO -e "\t$RUN_SCRIPT before_opkg_install | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT">>$POST_INSTALLER
        check_exit_code
    fi
    $BIN_ECHO -e "\tlocal PACKAGES=\"$PACKAGES\""\
            "\n\tlocal PACKAGE"\
            "\n\tlocal SCRIPT"\
            "\n\tfor PACKAGE in \$PACKAGES; do"\
            "\n\t\t$BIN_OPKG install \$PACKAGE | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT"\
            "\n\t\tfor SCRIPT in \$($BIN_OPKG files \$PACKAGE | $BIN_GREP /etc/init.d/); do"\
            "\n\t\t\tif [ -x \$SCRIPT ]; then"\
            "\n\t\t\t\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Executing \$SCRIPT enable for package \$PACKAGE\""\
            "\n\t\t\t\t\$SCRIPT enable | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT"\
            "\n\t\t\tfi"\
            "\n\t\tdone"\
            "\n\tdone">>$POST_INSTALLER
    check_exit_code
    if [ "$RUN_SCRIPT" != "" ] && [ -x $RUN_SCRIPT ]; then
        $BIN_ECHO -e "\t$RUN_SCRIPT after_opkg_install | $BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT">>$POST_INSTALLER
        check_exit_code
    fi
    if [ "$BACKUP_ENABLE" != "" ]; then
        $BIN_ECHO -e "\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Restoring configuration backup from $BACKUP_FILE\""\
                "\n\t$BIN_SYSUPGRADE --restore-backup $BACKUP_FILE">>$POST_INSTALLER
        check_exit_code
    fi
    $BIN_ECHO -e "\t$BIN_LOGGER -p user.notice -t $POST_INSTALL_SCRIPT \"Stop installation of packages, cleaning and force reboot\""\
            "\n\t$BIN_RM $POST_INSTALL_REMOVE"\
            "\n\t$BIN_RM -f $INSTALLER_KEEP_FILE"\
            "\n\t$BIN_AWK -v installer=\"$POST_INSTALLER\" '\$0!~installer' $RC_LOCAL>$RC_LOCAL.tmp"\
            "\n\t$BIN_MV -f $RC_LOCAL.tmp $RC_LOCAL"\
            "\n\t$BIN_RM -f $POST_INSTALLER"\
            "\nfi"\
            "\n$BIN_LOGREAD >>$POST_INSTALLER_LOG"\
            "\n$BIN_SYNC"\
            "\n$BIN_REBOOT -f"\
            "\n# Done.">>$POST_INSTALLER
    check_exit_code
    $BIN_CHMOD 777 $POST_INSTALLER
    check_exit_code
    add_to_keep_file $POST_INSTALLER
    [ "$RUN_SCRIPT" != "" ] && [ -x $RUN_SCRIPT ] && add_to_keep_file $RUN_SCRIPT
    $BIN_ECHO "Setting autorun packages installer on next boot in $RC_LOCAL ..."
    add_to_keep_file $RC_LOCAL
    $BIN_ECHO -e "[ -x $POST_INSTALLER ] && $POST_INSTALLER\n$($BIN_CAT $RC_LOCAL)">$RC_LOCAL
    check_exit_code
    add_to_post_installer_log "Packages installer prepared"
    $BIN_ECHO "Packages installer prepared."
}

sysupgrade_execute() {
    $BIN_ECHO "Upgrading system from image $INSTALL_PATH/$IMAGE_FILENAME ..."
    add_to_keep_file $0
    add_to_post_installer_log "Running system upgrade"
    cd $INSTALL_PATH
    $BIN_SYSUPGRADE $IMAGE_FILENAME
}

# Main routine
initialize $@
[ "$CMD" == "backup" ] && config_backup && exit
[ "$CMD" == "sysupgrade" ] && caution_alert
update_repository
check_installed
check_dependency
([ "$CMD" == "install" ] || [ "$CMD" == "sysupgrade" ]) && config_backup
[ "$CMD" == "install" ] && packages_disable && packages_install
([ "$CMD" == "download" ] || ([ "$CMD" == "sysupgrade" ] && [ "$OFFLINE_POST_INSTALL" != "" ])) && packages_download
[ "$CMD" == "install" ] && config_restore && packages_enable
([ "$CMD" == "download" ] || [ "$CMD" == "sysupgrade" ]) && image_download
[ "$CMD" == "sysupgrade" ] && installer_prepare && packages_disable && sysupgrade_execute
$BIN_ECHO "Done."
# Done.

Latest script code is here https://raw.github.com/Rafciq/openwrt/m … nstall.sh.

Before first use, put the scrpit code into file /sbin/install.sh on your router. Next step: you have to add configuration section to file /etc/config/system .
Example configuration for install.sh script:

config sysupgrade
    option localinstall '/install'
    option backupconfig '/backup'
    option imagesource 'http://ecco.selfip.net/<CODENAME>/<TARGET>/openwrt-<TARGET>-<SUBTARGET>-<HARDWARE>-squashfs-sysupgrade.bin'
    list opkg libusb
    list opkg kmod-usb-serial-option
    list opkg kmod-usb-net-cdc-ether

For typical system upgrade proces they are required two folders. One for save backup of router configuration (backupconfig) and second for download file firmware image and packeges files (localinstall). Both folders must be outside of router flash. I recomend pendrive plugged to router USB and auto-monted via fstab.
Option imagesource defines from the image file will be downloaded. Obligatory packages are defined by list opkg.
Variable used in imagesource path:

  • <CODENAME> name of milestone (ex. "attitude_adjustment")

  • <TARGET> platform name (ex. "ar71xx")

  • <SUBTARGET> sub-platform name (ex. "generic")

  • <HARDWARE> hardware name with version (ex. "tl-wdr4300-v1")

The install.sh script performs one of the four operations:

  1. install.sh sysupgrade

    • backup configuration,

    • download all packages and system image do install directory (in off-line mode),

    • prepare post upgrade package installer,

    • system upgrade,

    • ... reboot system ...,

    • if extroot exist, clean check sum and reboot system,

    • install packages,

    • restore configuration,

    • cleanup installation,

    • ... reboot system ...

  2. install.sh install

    • backup configuration,

    • stop and disable packages,

    • install packages,

    • restore configuration,

    • enable and start packages.

  3. install.sh download

    • download all packages and system image do install directory,

  4. install.sh backup

    • backup configuration

Other install.sh script command line options:

  • -b
    Disable configuration backup and restore during installation or system upgrade process.
    By default, backup and restore configuration are enabled.
    Path to backup have to on external device otherwise during system upgrade can be lost.

  • -o
    Online packages installation by post-installer.
    Internet connection is needed after system restart and before packages installation.

  • -i
    Exclude installed packages. Only packages from configuration can be processed.

Script Install.sh can execute your commands during the installation process.
You have to create your script, eg in the file /bin/extra_install.sh and add options to /etc/config/system.
Example configuration in file /etc/config/system:

config sysupgrade
    . . .
option runscript '/bin/extra_install.sh'

Example user script file /bin/extra_install.sh

#!/bin/sh

after_image_downloaded() {
    echo "after-image-downloaded"
}

before_opkg_update() {
    echo "before-opkg-update"
}

before_opkg_install() {
    echo "before-opkg-install"
}

after_opkg_install() {
    echo "after-opkg-install"
}

while [ -n "$1" ]; do 
    if type $1 | grep -q ' function'; then
        $1
    else
        echo "Invalid argument $1"
    fi
    shift 
done

* This install.sh script was tested with OpenWrt Attitude Adjustment 12.09.x but not with Gargoyle.

(Last edited by Rafciq on 7 Mar 2014, 20:12)

Rafciq wrote:

Script Install.sh can execute your commands during the installation process.
You have to create your script, eg in the file /bin/extra_install.sh and add options to /etc/config/system.
Example configuration in file /etc/config/system:

config sysupgrade
    . . .
option runscript '/bin/extra_install.sh'

Example user script file /bin/extra_install.sh

#!/bin/sh

after_image_downloaded() {
    echo "after-image-downloaded"
}

before_opkg_update() {
    echo "before-opkg-update"
}

before_opkg_install() {
    echo "before-opkg-install"
}

after_opkg_install() {
    echo "after-opkg-install"
}

while [ -n "$1" ]; do 
    if type $1 | grep -q ' function'; then
        $1
    else
        echo "Invalid argument $1"
    fi
    shift 
done

what is exactly that extra_install.sh kick in? whats difference between your script against the sysupgrade built-in command?

It still works on openwrt 15.05 and and lede 17.01.0.
I actually tried it from openwrt to lede and also from openwrt zbt wg 3526 to a lede dir 645!
thank you

I got Rafciq's script working on the LEDE community build I normally run davidc502sis.dynamic-dns.net

I had to change the following:

-the version of busybox ash in this stream does not like 'local' declaration of variables that aren't in a function so I removed the locals in both install.sh and the generated post-install script

-This build uses SHA256 instead of md5sums.  I haven't implemented SH256 yet in the script, for now I have just commented out all the md5 stuff.

-This community build does not properly register user installed packages in opkg, and there will be many packages showing as user installed that weren't actually user installed.  After quite a bit of experimentation, I finally settled on this method. gist.github.com/devkid/8d4c2a5ab62e690772f3d9de5ad2d978 That works by finding every package that was installed AFTER busybox.  It's slow but it works perfectly.

-The part of the script that sets the dependencies keeps setting libc as a dependant package needing installed post installation.  I explicitly remove libc from the generated list, as in this build there actually is no libc to download and it is in the manifest anyway

@fiver

Care to share your updated script? Really need this kind of tool right now.

Redferne wrote:

@fiver

Care to share your updated script? Really need this kind of tool right now.

There are a few things I do that are a bit hacky and I think there are some things that are specific to my environment.  I was going to try and go through and make sure everythign was variabilized and portable before putting it out, but here it is as it stands right now.

pastebin.com/1zTX4JJ9

Thanks! I will have look. I think this is a crucial part missing in openwrt world. A sane way to do one-click upgrades of image/settings/packages.

the script looks interesting. i had no time to look in detail but i have a few questions though...
download & backup are all made on some external storage. my router can't, by default, mount any external storage without some packages installed. is this taken care of by the script?
what do i need to set up? configuration in system cfg file and put the script in some directory. i got that but anything else?

by the way, this needs to be part of the default openwrt/lede image as it's very practical!

Hello and greetings from Germany,
Apologize my bad english and I hope you understand me anyway.

Does the script of Rafciq  still work? Especially with the version LEDE 17.01.1 on a TL-WR1043ND V3?

If not, it could edit someone of you who has enough knowledge of it?
And is automatically searched for a usb stick?

After some hours reading and testing, I have managed to use the Extroot configuration for lede
I would like to use it for the future and would be very grateful if you can help me.

Best regards