0

Error

While running the sudo apt update and sudo apt upgrade on a new Ubuntu Server 20.04.2 LTS 32-bit server OS for armhf architectures on the RaspberryPi with 4gb ram, I am encountering the following errors:

For sudo apt update:

E: Release file for http://ports.ubuntu.com.../focal-updates/Inrelease is not yet valid (invalid for another 113d 22h 43min). Updates for this repository will not be applied.

For sudo apt upgrade:

Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 3381 (unattended-upgr).

Code

The code below first checks if internet is available, if it is not, it checks if the wifi ssid and password have been added to the configuration, if not it prompts the user for them, and then activates those settings until the internet connection is established. Then it performs the sudo apt update and sudo apt upgrade commands.

# Verify internet access is available
while [ $(ping -q -w1 -c1 google.com &>/dev/null && echo online || echo offline) == offline ]
do

    sleep 1
    # get wifi module
    module=$(ls /sys/class/net)
    module_list=($module)
    wlan_module=${module_list[-1]}
    echo $wlan_module

    # get wifi configuration file
    config_filename=$(ls /etc/netplan)
    echo $config_filename
    config_filepath="/etc/netplan/$config_filename"
    
    # check if the wifi is already added.
    if ! grep -q "$wlan_module" "$config_filepath"; then
        echo "have to add wifi"

        # ask  wifi pwd ssid
        read -p "What is the wifi ssid you want to connect to(Case sensitive)?" ssid

        # ask wifi pwd
        read -p "What is the wifi pwd?" pwd

        # append content
        echo "    wifis:" | sudo tee -a $config_filepath 
        echo "        wlan0:" | sudo tee -a $config_filepath
        echo "            dhcp4: true" | sudo tee -a $config_filepath
        echo "            optional: true" | sudo tee -a $config_filepath
        echo "            access-points:" | sudo tee -a $config_filepath
        echo "                \"$ssid\":" | sudo tee -a $config_filepath
        echo "                    password: \"$pwd\"" | sudo tee -a $config_filepath

        # generate a config file
        sudo netplan generate

        # apply the config file
        sudo netplan apply
        
    fi
done

# set timezone based on ip
export tz=`wget -qO - http://geoip.ubuntu.com/lookup | sed -n -e 's/.*<TimeZone>\(.*\)<\/TimeZone>.*/\1/p'` &&  timedatectl set-timezone $tz
export tz=`timedatectl status| grep Timezone | awk '{print $2}'`

# Verify timezone is set correctly.
timedatectl status

yes | sudo apt update
read -p "Update completed <type enter>" next
yes | sudo apt upgrade
read -p "Upgrade completed <type enter>" next

the code is executed using:

sudo mkdir /media/usbstick
sudo mount /dev/sda1 /media/usbstick
/media/usbstick/.first_run.sh

Timezone details

In response to the comments I have included code that automatically sets the timezone, and I have manually verified the timezone is set correctly.

Assumption

I assume that that is because the Ubuntu server is automatically performing some kind of update/upgrading in the background in the background, which prevents me from running the sudo apt upgrade command simultaneously.

Question

So when I manually kill the process that blocks the sudo apt upgrade command with sudo kill -9 <process_id> I am able to successfully run the sudo apt upgrade command. However, I can imagine this is not the best way to ensure the upgrade command is completed successfully. So my current approach is to write a while-loop that scans the output of the sudo apt upgrade command until it yields a successful output. However, that feels like re-inventing the wheel and I can imagine there might exist better ways to resolve this issue. Hence, I would like to ask:

How can I run the sudo apt upgrade command safely and automatically on a new installation of Ubuntu Server 20.04 LTS 32-bit server such that the rest of my Ubuntu Server post script can be completed correctly?

a.t.
  • 275
  • 4
  • 13
  • 2
    Is your system's date/time and timezone correct? You should consider adding commands to correct the system time if it is not correct. – FedKad May 26 '21 at 11:38
  • 1
    Double check the date on your system anyways - you might need to temporarily manually set the date on the system by hand first. `timedatectl status` will get you the current date/time on the system – Thomas Ward May 26 '21 at 12:16
  • In response to your comments I included a code that automatically sets the time zone, and I have manually verified the `Local time:`, `Universal time:` and `Time zone:` are set correctly. The output errors remain the unchanged. So to explicitly answer the question by @FedonKadifeli, yes the date/time and timezone are correct. – a.t. May 26 '21 at 13:53
  • I had this issue once, but after update and upgrade finally ran, I never had the issue again. Are you still having this issue? – Gordster May 26 '21 at 13:57
  • @Gordster after the update and upgrade commands are ran successfully, the error indeed disappears. Since this is the first script I intend to run after a new installation, this question pertains to automatically setting up that first run. – a.t. May 26 '21 at 14:25

1 Answers1

0

My current implementation consists of two while loops that run the two sudo apt update and sudo apt upgrade commands, until their respective outputs are verified. It is far from elegant but appear to be working. The enhanced first_run.sh script is contained below:

# Assumes both strings have equal lengths
equal_strings() {
    left=$1
    right=$2
    
    # Initialise the default test result to false
    test_result=false
    
    # Set the test result to true if the left and right string are equal
    if [ "$left" = "$right" ]; then
        echo "Strings are equal."
        test_result=true
    else
            echo "Strings are not equal."
    fi
    
    # Output true or false to pass the equality test result to parent function
    echo $test_result
}


# Assumes the actual result is shorter than the allowed result
right_is_in_tail_of_left() {
    left=$1
    right=$2
    right_size=${#right}
    left_tail_chars=$(echo -n $left | tail -c $right_size)

    # Output true or false to pass test result to parent function
    echo $(equal_strings "$left_tail_chars" "$right")
}


# Makes the main script runnable, removes the log file and runs main file.
actual_result_has_any_allowed_result_in_tail() {
    # declare default function output
    test_result=false
    
    # get the actual result
    actual_result=$1
    shift # Shift all arguments to the left (original $1 gets lost)
    
    # get the list of allowed results and list size
    allowed_results=("$@") 
    list_size=${#allowed_results}

    # Only engage this function if the list size is greater than 1
    if [ $list_size -gt 1 ]; then echo "error";
    
        # if the actual result is in the acceptable list ensure function returns true
        for allowed_result in "${allowed_results[@]}"
        do
            if [ $test_result != true ]; then
                # compute lengths of result strings
                allowed_result_size=${#allowed_result}
                actual_result_size=${#actual_result}
                
                # TODO: remove this if condition and directly go to Else by switching lt to ge
                if [ $actual_result_size -lt $allowed_result_size ]; then echo "error";
                    echo "The actual size is:"
                    echo $actual_result_size
                    echo "WHEREAS allowed_result_size=" 
                    echo $allowed_result_size
                    echo "so actual is smaller than allowed, so do nothing"
                else 
                    # test if left string is in the tail of the allowed result string
                    # TODO: include contains option in separate function
                    temp_test_result=$(right_is_in_tail_of_left "$actual_result" "$allowed_result");
                    if [ $(echo -n $temp_test_result | tail -c 4) == true ]; then
                        test_result=true
                    fi
                fi
            fi
        done
    fi
        
    # Ensure the last 4/5 characters of the output of this function contains the true false evaluation.
    echo $test_result
}




# Verify internet access is available
while [ $(ping -q -w1 -c1 google.com &>/dev/null && echo online || echo offline) == offline ]
do

    sleep 1
    # get wifi module
    module=$(ls /sys/class/net)
    module_list=($module)
    wlan_module=${module_list[-1]}
    echo $wlan_module

    # get wifi configuration file
    config_filename=$(ls /etc/netplan)
    echo $config_filename
    config_filepath="/etc/netplan/$config_filename"
    
    # check if the wifi is already added.
    if ! grep -q "$wlan_module" "$config_filepath"; then
        echo "have to add wifi"

        # ask  wifi pwd ssid
        read -p "What is the wifi ssid you want to connect to(Case sensitive)?" ssid

        # ask wifi pwd
        read -p "What is the wifi pwd?" pwd

        # append content
        echo "    wifis:" | sudo tee -a $config_filepath 
        echo "        wlan0:" | sudo tee -a $config_filepath
        echo "            dhcp4: true" | sudo tee -a $config_filepath
        echo "            optional: true" | sudo tee -a $config_filepath
        echo "            access-points:" | sudo tee -a $config_filepath
        echo "                \"$ssid\":" | sudo tee -a $config_filepath
        echo "                    password: \"$pwd\"" | sudo tee -a $config_filepath

        # generate a config file
        sudo netplan generate

        # apply the config file
        sudo netplan apply
        
    fi
done

# https://askubuntu.com/questions/323131/setting-timezone-from-terminal
echo "Setting TimeZone..."
export tz=`wget -qO - http://geoip.ubuntu.com/lookup | sed -n -e 's/.*<TimeZone>\(.*\)<\/TimeZone>.*/\1/p'` &&  timedatectl set-timezone $tz
export tz=`timedatectl status| grep Timezone | awk '{print $2}'`
echo "TimeZone set to $tz"

# A loop that checks whether the output of the update command is as expected after successfull completion.
check_output_update_command() {
    #test_result=false
    while [[ $test_result != true ]]
    do
        update_output=$(yes | sudo apt update)
        echo $update_output

        allowed_results=("Reading package lists... Building dependency tree... Reading state information... All packages are up to date."
                "packages can be upgraded. Run 'apt list --upgradable' to see them."
            )
            
        test_result=$(actual_result_has_any_allowed_result_in_tail "$update_output" "${allowed_results[@]}")
        test_result=${test_result: -4}
        echo $test_result
    done
}
check_output_update_command "$@"
read -p "Succesfully completed update command <type enter>" next

# A loop that checks whether the output of the upgrade command is as expected after successfull completion.
check_output_upgrade_command() {
    #output_ending=false
    while [[ $output_ending != " upgraded." ]]
    do
        upgrade_output=$(yes | sudo apt upgrade)
        echo $upgrade_output

        
        #output_ending=$(tail -c 11 $upgrade_output)
        output_ending=${upgrade_output: -10}
        echo $output_ending
    done
}
check_output_upgrade_command "$@"
read -p "Succesfully completed upgrade command <type enter>" next

And it can be executed using:

sudo mkdir /media/usbstick
sudo mount /dev/sda1 /media/usbstick
/media/usbstick/.first_run.sh
a.t.
  • 275
  • 4
  • 13