Compare commits

...

14 Commits

9 changed files with 468 additions and 728 deletions

View File

@ -1,11 +1,10 @@
{
"fifo_dir" : "/tmp",
"fifo" : "update_controller_fifo",
"workspace" : "workspace",
"GIT_SSL_NO_VERIFY" : true,
"working_directory" : ".",
"workspace_dir" : "workspace",
"customer_location" : "szeged",
"customer_id" : "281",
"zone_group" : "1",
"zone" : "1",
"customer_location" : "szeged",
"repository_path" : "https://git.mimbach49.de/GerhardHoffmann"
}

View File

@ -1,396 +0,0 @@
#!/bin/bash
set -x
fifo=""
customer_id=""
customer_repository=""
customer_location=""
customer_base_dir=""
source ./log_helpers.sh
source ./update_helpers.sh
source ./git_helpers.sh
source ./opkg_helpers.sh
init_fifo () {
trap "rm -f $fifo" EXIT
if [ $? -eq 0 ]; then
# set trap
if [[ ! -p "$fifo" ]]; then
mkfifo "$fifo"
if [ $? -eq 0 ]; then
# fifo created
echo "created fifo=$fifo"
return 0
fi
fi
fi
return 1
}
read_config() {
local func="${FUNCNAME[0]}"
# check fifo settings
fifo_dir=$(cat "$1" | jq -r .fifo_dir)
if [ -z "$fifo_dir" ]; then
log_fatal "$func: fifo_dir not set in $1"
fi
fifo=$(cat "$1" | jq -r .fifo)
if [ -z "$fifo" ]; then
log_fatal "$func: fifo not set in $1"
fi
fifo="${fifo_dir}/$fifo"
log_debug "$func:${LINENO}: fifo is $fifo"
# check customer_id
customer_id="$(cat "$1" | jq -r .customer_id)"
if [ -z "$customer_id" ]; then
log_fatal "$func:${LINENO} customer_id not set in $1"
fi
customer_id="customer_$customer_id"
log_info "$func:${LINENO}: customer-id is $customer_id"
# check customer_repository
repository_path="$(cat "$1" | jq -r .repository_path)"
if [ -z "$repository_path" ]; then
log_fatal "$func:${LINENO}: repository path not set in $1"
fi
repository_path="${repository_path}/${customer_id}.git"
log_info "$func:${LINENO}: repository path is $repository_path"
# check zone_group
local zone_group="$(cat "$1" | jq -r .zone_group)"
if [ -z "$zone_group" ]; then
log_fatal "$func:${LINENO}: zone_group not set in $1"
fi
# check zone
local zone="$(cat "$1" | jq -r .zone)"
if [ -z "$zone" ]; then
log_fatal "$func:${LINENO}: zone not set in $1"
fi
# check customer_location
local customer_location="$(cat "$1" | jq -r .customer_location)"
if [ -z "$customer_location" ]; then
log_fatal "$func:${LINENO}: customer_location not set in $1"
fi
customer_base_dir="$PWD/workspace/${customer_id}/${customer_location}"
customer_base_dir="${customer_base_dir}/${zone_group}/${zone}"
log_info "$func:${LINENO}: customer-base-dir is $customer_base_dir"
return 0
}
wait_for_trigger() {
local func="${FUNCNAME[0]}"
if [[ "$1" -eq -1 ]]; then
# wait for external impulse
log_debug "$func:${LINENO}: waiting on $fifo"
if read line <$fifo; then
if [ "$line" = "quit" ] || [ "$line" = "update" ]; then
log_debug "$func:${LINENO}: received=$line"
printf "%s\n" $line
fi
fi
else
sleep $1
log_debug "$func:${LINENO}: sleep"
fi
}
# APISM has al least two TCP-ports open: 7778 and 7779. To check for a running
# APISM it is therefore enough to check for the port 7778. netcat will return 0
# if the port 7778 is open.
check_for_apism() {
return 0
nc localhost 7778
if [ $? -eq 0 ]; then
return 0
fi
return 1
}
######################### send_to_ismas.txt ###################################
# #M=GH_APP
# #C=REQ_ISMASParameter
# #J={}
# {
# "DEV_ID": {
# "Device_Type": "2020",
# "Custom_ID": 999,
# "Device_ID": 1
# },
# "REQ_ISMASParameter_#1": {}
# }
#
# APISM should respond with (something like this):
#
# {
# "REQ_ISMASPARAMETER#69553_Response": {
# "Aknoledge": "OK"
# },
# "Dev_ID": {
# "Device_Type": "ISMAS",
# "Custom_ID": 999,
# "Device_ID": 2
# },
# "Fileupload": [
# { "IPK": "BladiBlupp.Zip",
# "MD5": "01234567890123456789012345678901"}
# ],
# "Parameter": {
# "Location": "An der Bahn 11",
# "Group": "G1",
# "Zone": "Z1",
# "Name": "Bruno",
# "SecNumber": "0",
# "LastAcc": "1",
# "GPSLat": "49,6019",
# "GPSLon": "12,1258"
# }
# }
#
# where the interesting part is contained in "FileUpload".
#
check_for_updates() {
# ismas_response=$(cat send_to_ismas.txt; sleep 1) | nc localhost 7778)
if ! pull_customer_repository customer_281; then
return 1
fi
return 0
}
backup_previous_version() {
return 0
}
fetch_updates() {
return 0
}
check_updates() {
return 0
}
revert_updates() {
return 0
}
lock_before_installing_updates() {
return 0
}
do_update_dry_run() {
return 0
}
do_update() {
return 0
}
fallback_to_previous_version() {
return 0;
}
unlock_after_installing_updates() {
return 0;
}
cleanup_previous_version() {
return 0
}
send_reboot_message_to_system_controller() {
return 0
}
###############################################################################
#
# UPDATE CONTROLLER
#
###############################################################################
UpdateController() {
local trigger_reboot=false
local check_apism_count=0
local try_update_count=0
local try_lock_count=0
local try_unlock_count=0
local trigger_timeout=-1
local func="${FUNCNAME[0]}"
# read config parameters
# read_config
# By default (trigger_timeout==-1), UpdateController can only be triggered
# from some external source to perform an update.
while :
do
trigger=$(wait_for_trigger $trigger_timeout)
echo "trigger=$trigger"
if [ "$trigger" = "quit" ]; then
$trigger_reboot = true
elif [ "$trigger" != "update" ]; then
continue
fi
if $trigger_reboot; then
log_crit "$func:${LINENO}: send message to reboot the PSA"
send_reboot_message_to_system_controller
continue
fi
# Is APISM running, listening on the correct ports etc. ?
while :
do
if ! check_for_apism; then
var=$((var+1))
check_apism_count=$((check_apism_count+1))
if [[ "$check_apism_count" -eq 5 ]]; then
trigger_reboot=true
check_apism_count=0
log_crit "$func:${LINENO}: ERROR APISM not working"
continue 2
fi
log_debug "$func:${LINENO}: [$check_apism_count]: check APISM"
sleep 60s
else
# APISM up and working
check_apism_count=0
break
fi
done
log_debug "$func:${LINENO}: checking for updates..."
# exit 1
# Are there new updates available ?
if ! check_for_updates; then
log_info "$func:${LINENO}: DEBUG no updates available"
extract_changed_files
continue
fi
log_debug "$func:${LINENO}: fetching updates..."
exit 1
# Fetch new updates (using git)
while :
do
if ! fetch_updates; then
try_updates_count=$((try_updates_count+1))
if [[ "$try_updates_count" -eq 5 ]]; then
trigger_reboot=true
echo "ERROR fetching updates"
continue 2
fi
sleep 60s
else
# Fetched updates successfully
try_updates_count=0
break
fi
done
# Check the updates for:
# * correct MD5
# * compatibility with PSA
# ...
if ! check_updates; then
echo "ERROR check_updates"
revert_updates
trigger_reboot=true
continue
fi
# perform a dry-run and check if everything might work as expected.
if ! do_update_dry_run; then
echo "ERROR do_update_dry_run"
revert_updates
trigger_reboot=true
continue
fi
# Backup before any updates in case some previous test was wrong
if ! backup_previous_version; then
echo "ERROR backup_previous_version"
revert_updates
trigger_reboot=true
continue
fi
# Lock the PSA so all other tasks on the PSA are in sync with the update
while :
do
if ! lock_before_installing_updates; then
try_lock_count=$((try_lock_count+1))
if [[ "$try_lock_count" -eq 5 ]]; then
revert_updates
fallback_to_previous_version
trigger_reboot=true
echo "ERROR locking PSA"
continue 2
fi
sleep 60s
else
# Locked PSA successfully
try_lock_count=0
break
fi
done
# Actually do the update
if ! do_update; then
echo "ERROR do_update"
revert_updates
fallback_to_previous_version
trigger_reboot=true
continue
fi
while :
do
# If all went well, the PSA can return to normal operation
if ! unlock_after_installing_updates; then
try_unlock_count=$((try_unlock_count+1))
if [[ "$try_unlock_count" -eq 5 ]]; then
revert_updates
fallback_to_previous_version
trigger_reboot=true
echo "ERROR unlocking PSA"
continue 2
fi
sleep 60s
else
# Unlocked PSA successfully
try_unlock_count=0
break
fi
done
# Cleanup.
if ! cleanup_previous_version; then
echo "ERROR cleanup_previous_version"
fi
done
}
###############################################################################
if [ $# -ne 1 ] ; then
echo "Usage: $0 filename"
exit 1
else
if read_config "$1" ; then
init_fifo
UpdateController
fi
fi

View File

@ -1,17 +1,25 @@
#!/bin/bash
# set -x
source ./log_helpers.sh
# if [ ${git_helpers_sourced:-1} = "1" ]; then
# readonly git_helpers_sourced=${BASH_SOURCE[0]}
#else
# return 0
#fi
GIT_SSL_NO_VERIFY=true
# source ./log_helpers
readonly GIT_SSL_NO_VERIFY=true
readonly repository_already_up_to_date=2
#
exec_git_command () {
local func="${FUNCNAME[0]}"
log_debug "$func:${LINENO} exec-ing $1"
log_debug "$func:${LINENO} exec-ing [$*]"
exec {fd}< <($1)
exec {fd}< <($@)
local ps_pid=$! # remember pid of process substitution
local git_result=""
@ -35,15 +43,13 @@ latest_commit () {
local c=$(git reflog | grep "HEAD@{0}" | cut -d" " -f1)
if ! [ -z "$c" ]; then
if grep -qE "^[[:xdigit:]]{6,}$" <<< $c; then
log_debug "$func: commit -> $c"
log_debug "$func:${LINENO} commit -> $c"
printf "%s\n" $c
else
log_crit "$func: wrong format for commit c=$c"
printf ""
log_crit "$func:${LINENO} wrong format for commit c=$c"
fi
else
log_crit "$func: 'git reflog' result empty"
printf ""
log_crit "$func:${LINENO} 'git reflog' result empty"
fi
}
@ -79,10 +85,22 @@ clone_customer_repository () {
# check if the directory is empty. If so, clone the
# customer repository
if ! find ./workspace -mindepth 1 -maxdepth 1 | read; then
log_debug "$func: cloning ${1} ..."
( cd ./workspace && git clone "$1" ; )
if [ $? -eq 0 ]; then
log_debug "$func: cloning ${1} done"
log_debug "$func:${LINENO} cloning ${1} ..."
if { cd ./workspace ; }; then
$(exec_git_command git clone "$1")
if [ $? -eq 0 ]; then
log_debug "$func:${LINENO} cloning ${1} done"
cd - ; return 0
fi
cd -
fi
else
# the directory is not empty, so we assume the customer-repository
# has been cloned alread
if ! [[ -d ./workspace/$customer_id ]]; then
local m="wrong repository: $(ls -d './workspace/*')"
log_fatal "$func:${LINENO} $m"
else
return 0
fi
fi
@ -104,7 +122,7 @@ cd_customer_repository () {
return 1
fi
if ! cd $repository_dir; then
if ! { cd $repository_dir; } ; then
log_crit "$func:${LINENO}: cannot change to $repository_dir!"
return 1
fi
@ -137,28 +155,21 @@ pull_customer_repository () {
log_debug "$func:${LINENO}: commit_before_pull=$commit_before_pull"
exec {fd}< <(git pull)
local ps_pid=$! # remember pid of process substitution
local git_result=""
while read t <&$fd; do
if ! [ -z "$t" ]; then
git_result="${git_result}$t"
fi
done
exec {fd}>&- # close fd (i.e. process substitution)
wait $ps_pid # wait for the subshell to finish
local git_result=$(exec_git_command 'git pull')
if [ -z "$git_result" ]; then
log_warn "$func:${LINENO}: git result empty" ; cd_home;
return 1
fi
# see 'man -Pless\ +/parameter/pattern/string/bash'
git_result=${git_result//[$'\r\n\t']/ }
log_debug "$func:${LINENO} git-pull-result=${git_result}"
if [ "$git_result" = "Already up to date." ]; then
log_warn "$func:${LINENO}: $PWD already up to date."
if grep -qE "^Already\s+\up\s+\to\s+date.*$" <<< $git_result; then
log_warn "$func:${LINENO}: repository $PWD already up to date."
read $1 <<< 'yes'
cd_home ; return 1
fi
@ -228,14 +239,26 @@ pull_customer_repository () {
cd_home ; return 1
fi
return 0
cd_home ; return 0
}
# pull_customer_repository customer_281
changed_files () {
changed_file_names () {
local func="${FUNCNAME[0]}"
if cd_customer_repository ; then
printf "%s\n" $(exec_git_command 'git diff --stat master@{1} master')
cd_home
local git_res=$(exec_git_command 'git diff --stat master@{1} master')
git_res=${git_res//[$'\r\n\t']/ }
log_debug "$func:${LINENO}: git_res=$git_res"
local file_names=""
for f in 'update.conf' 'opkg_commands' ; do
if grep -qE ".*${f}.*?" <<< $git_res; then
file_names="$f $file_names"
fi
done
cd_home ; printf "$file_names"
else
log_crit "$func:${LINENO}: cannot cd to $customer_repository "\
"while in $PWD"
fi
}

View File

@ -1,12 +1,24 @@
#!/bin/bash
# set -x
#if [ ${log_helpers_sourced:-1} = "1" ]; then
# readonly log_helpers_sourced=${BASH_SOURCE[0]}
#else
# return 0
#fi
readonly log_file=/var/log/update_controller.log
if ! [ -f "$log_file" ]; then
touch $log_file
fi
readonly DEBUG=0
readonly INFO=1
readonly WARN=2
readonly CRIT=3
readonly FATAL=4
readonly MAX_DEBUG_LEVEL=5
readonly ERROR=4
readonly FATAL=5
readonly MAX_DEBUG_LEVEL=6
log_level=0
@ -21,17 +33,12 @@ dbg_level () {
}
log() {
local log_file=/var/log/update_controller.log
if [ -f "$log_file" ]; then
touch $log_file
fi
if [[ "$(wc -l < $log_file)" > "100000" ]]; then
if [[ $(("$(wc -l < $log_file)")) -ge $((100000)) ]]; then
# remove first line
sed -e 1d -i $log_file
fi
local msg="$(date +'%Y-%m-%d_%T'): $*"
printf "$msg\n" >&2
printf "log:$msg\n" >&2
printf "$msg\n" >> $log_file
}
@ -59,6 +66,12 @@ log_crit() {
fi
}
log_error() {
if [ $log_level -le $ERROR ]; then
log "ERROR $*"
fi
}
log_fatal() {
if [ $log_level -le $FATAL ]; then
log "FATAL $*"

283
opkg.sh
View File

@ -1,283 +0,0 @@
#!/bin/bash
# set -x
compute_md5 () {
execdownload_opkg {fd}< <(md5sum "$1") # open fd readable for P.S.
cs_pid=$! # remember pid of P.S.
md5=""
while read t <&$fd; do
if ! [ -z "$t" ]; then
t=($t) # md5sum returns two values:
# the hash plus the filename
md5="${md5}$t"
fi
done
exec {fd}>&- # close fd (i.e. process substitution)
wait $cs_pid # wait for the subshell to finish
echo $md5
}
md5_for_ini_files() {
ini_directory="$1"
ini_files=("ISMASMgr.ini" "sysconfig.ini" "SystemControl.ini")
local data="\"ini\": {"$'\n'
for f in "${ini_files[@]}"; do
ini_file=${ini_directory}/$f
md5=`compute_md5 $ini_file`
data+="\"$ini_file\":\"$md5\","
done
if [[ "$data" =~ ^.*,$ ]]; then
data=${data%?}
fi
data+="},"
echo $data
}
md5_for_conf_files() {
conf_directory="$1"
conf_files=("emp.conf" "printer.conf" "device.conf")
local data="\"conf\": {"$'\n'
for f in "${conf_files[@]}"; do
conf_file=${conf_directory}/$f
md5=`compute_md5 $conf_file`
data+="\"$conf_file\":\"$md5\","
done
if [[ "$data" =~ ^.*,$ ]]; then
data=${data%?}
fi
data+="},"
echo $data
}
write_config() {
local path="$1"
if [ "${path: -1}" == "/" ]; then # remove trailing '/'
path=${path%?}
fi
local data="{ "$'\n'
data+=`md5_for_ini_files "$path/opt/app/sysconfig"`
data+=`md5_for_conf_files "$path/etc/psa_config"`
data+="
\"opkg\" : {"
local cnt=0
for package_name in $(opkg list-installed); # list names of installed packages
do
if ! grep -q "^[a-zA-Z]" <<< $package_name; then
continue
fi
if [ -z "$package_name" ]; then
continue
fi
cnt=$((cnt+1))
printf "%3d:%s\n" "$cnt" "$package_name"
# Format of package info:
#
# Package: openssh-scp
# Version: 7.8p1+git-r0
# Depends: libc6 (>= 2.28), update-alternatives-opkg
# Status: install ok installed
# Section: console/network
# Architecture: cortexa9t2hf-neon
# Maintainer: Poky <poky@yoctoproject.org>
# MD5Sum: dfffcbb088cd5f180be5e9ee2ad030fe
# Size: 32700
# Filename: openssh-scp_7.8p1+git-r0_cortexa9t2hf-neon.ipk
# Source: openssh_7.8p1+git.bb
# Description: A suite of security-related network....
# Installed-Size: 58920
# Installed-Time: 1654699615
#
# process substitution (P.S):
# run 'opkg info' synchronously to parent script
exec {fd}< <(opkg info "$package_name") # open fd readable for P.S.
cs_pid=$! # remember pid of P.S.
while read package_info_line <&$fd; do
if [[ $package_info_line == Package* ]]; then
package=(${package_info_line// / })
package="\"${package[1]}\": {"
elif [[ $package_info_line == MD5Sum* ]]; then
md5sum=(${package_info_line// / })
md5sum="\"${md5sum[0]%?}\": \"${md5sum[1]}\","
elif [[ $package_info_line == Filename* ]]; then
filename=(${package_info_line// / })
filename="\"${filename[0]%?}\": \"${filename[1]}\","
elif [[ $package_info_line == Installed-Time* ]]; then
installed_time=(${package_info_line// / })
installed_time="\"${installed_time[0]%?}\": \"${installed_time[1]}\"
},"
fi
done
exec {fd}>&- # close fd (i.e. process substitution)
wait $cs_pid # wait for the subshell to finish
# use 8 spaces
data="$data
$package
$md5sum
$filename
$installed_time"
# break
done
if [[ "$data" =~ ^.*,$ ]]; then
data=${data%?}
fi
data="$data
}
}"
echo "$data" > "${path}/$2"
# echo $(cat "/tmp/test.txt" | jq -C --indent 4 '.') > /tmp/test2.txt
}
# write_config "/home/root/szeged/customer_281/szeged/1/1/" "test.txt"
# Download opkg-package. After the download, extract data.tar.gz to
# access e.g. atbqt.
download_opkg_package () {
case "$1" in
atbqt) # hier weitere packete eintragen
;;
*)
printf "ERROR $1 has wrong format\n" >&2
return 1
;;
esac
exec {fdd}< <(opkg --download-only download "$1") # open fd readable for P.S.
cs_pid=$! # remember pid of P.S.
opkg_result=""
while read result <&$fdd; do
if ! [ -z "$result" ]; then
opkg_result+="$result"
fi
done
exec {fdd}>&- # close fd (i.e. process substitution)
wait $cs_pid # wait for the subshell to finish
if [ -z "$opkg_result" ]; then
printf "ERROR empty opkg_result\n" >&2
return 1
fi
if ! grep -qF "$1" <<< $opkg_result; then
printf "ERROR opkg_result [$opkg_result] does not contain $1\n" >&2
return 1
fi
if ! grep -qF "$2" <<< $opkg_result; then
printf "ERROR opkg_result [$opkg_result] does not contain $2\n" >&2
return 1
fi
# printf "%s\n" $opkg_result
return 0
}
# download_opkg_package "atbqt" "atbqt_1.0.0-r1_cortexa9t2hf-neon.ipk"
ar_extract_opkg_package_data () {
return 0
cp $1 "/tmp"
# mv $1 "/tmp"
cd /tmp
# ar -x $1
#if [ $? ne 0 ]; then
data_file="data.tar.xz"
if ! [ -f "$data_file" ]; then
printf "ERROR %s does not exist\n" "$data_file" >&2
return 1
fi
cd -
return 0
tar -qxvf $data_file
echo $pwd
}
# ar_extract_opkg_package_data "atbqt_1.0.0-r1_cortexa9t2hf-neon.ipk"
tar_extract_opkg_package_data () {
exec {ftar}< <(tar -xvf "$2" -C "$1") # use process substitution
cs_pid=$!
exec {ftar}>&-
wait $cs_pid
binary="$1/$3"
if ! [ -f "$binary" ]; then
printf "ERROR %s does not exist\n" "$binary" >&2
return 1
fi
return 0
}
# tar_extract_opkg_package_data "/tmp" "data.tar.xz" "opt/app/ATBAPP/ATBQT"
read_atbqt_version () {
version=$(/tmp/opt/app/ATBAPP/ATBQT -v)
version=(${version// / })
# ATB APP: Version: 20221220_1038_00318q git describe: 4.5.2-65-gf1799aa3_00318/Nexobility_dev
# printf "%s\n" "${version[@]}"
if [ ${#version[@]} -lt 7 ]; then
printf "ERROR %s has wrong format\n" "${version[@]}" >&2
return 1
fi
echo "${version[3]} ${version[6]}"
}
# xxx=`read_atbqt_version`
# echo $xxx
# set -x
check_atbqt_version () {
if ! `download_opkg_package "$1" "$2"`; then
return 1
fi
if ! `ar_extract_opkg_package_data "$2"`; then
return 1
fi
if ! `tar_extract_opkg_package_data "/tmp" "data.tar.xz" "opt/app/ATBAPP/ATBQT"`; then
return 1
fi
version=`read_atbqt_version`
version=(${version// / })
if [ "${version[0]}" = "$3" ] && [ "${version[1]}" = "$4" ]; then
return 0
fi
return 1
}
# check_atbqt_version "atbqt" \
# "atbqt_1.0.0-r1_cortexa9t2hf-neon.ipk" \
# "20221220_1038_00318q" \
# "4.5.2-65-gf1799aa3_00318/Nexobility_dev"

64
read_config Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
# set -x
#if [ ${read_config_sourced:-1} = "1" ]; then
# readonly read_config_sourced=${BASH_SOURCE[0]}
#else
# return 0
#fi
# source ./log_helpers
# read config file (JSON syntax)
#
read_config() {
local func="${FUNCNAME[0]}"
# check customer_id
__customer_id="$(cat "$1" | jq -r .customer_id)"
if [ -z "$__customer_id" ]; then
log_fatal "$func:${LINENO} customer_id not set in $1"
fi
readonly customer_id="customer_${__customer_id}"
log_info "$func:${LINENO}: customer-id is $customer_id"
# check customer_repository
__repository_path="$(cat "$1" | jq -r .repository_path)"
if [ -z "$__repository_path" ]; then
log_fatal "$func:${LINENO}: repository path not set in $1"
fi
readonly repository_path="${__repository_path}/${customer_id}.git"
log_info "$func:${LINENO}: repository path is $repository_path"
# check zone_group
local zone_group="$(cat "$1" | jq -r .zone_group)"
if [ -z "$zone_group" ]; then
log_fatal "$func:${LINENO}: zone_group not set in $1"
fi
# check zone
local zone="$(cat "$1" | jq -r .zone)"
if [ -z "$zone" ]; then
log_fatal "$func:${LINENO}: zone not set in $1"
fi
# check customer_location
local customer_location="$(cat "$1" | jq -r .customer_location)"
if [ -z "$customer_location" ]; then
log_fatal "$func:${LINENO}: customer_location not set in $1"
fi
local workspace="$(cat "$1" | jq -r .workspace_dir)"
__customer_base_dir="$PWD/${workspace}/${customer_id}/${customer_location}"
readonly customer_base_dir="${__customer_base_dir}/${zone_group}/${zone}"
log_info "$func:${LINENO}: customer-base-dir is $customer_base_dir"
readonly opkg_cmds_file="${customer_base_dir}/etc/psa_update/opkg_commands"
log_info "$func:${LINENO}: opkg_cmds_file is $opkg_cmds_file"
return 0
}
# read_config UpdateController.conf

View File

@ -1,8 +1,8 @@
#!/bin/bash
# set -x
source ./log_helpers.sh
source ./git_helpers.sh
source ./log_helpers
source ./git_helpers
extract_changed_files () {
local func="${FUNCNAME[0]}"

163
update_psa Executable file
View File

@ -0,0 +1,163 @@
#!/bin/bash
# set -x
source ./log_helpers
source ./git_helpers
# source ./update_helpers.sh
# source ./opkg_helpers.sh
source ./update_psa_helpers
source ./read_config
###############################################################################
# update_psa
#
# Implementing the UpdateController (see below). UpdateController is waiting
# for an external trigger by the DeviceController (DC). The trigger is sent via
# an UNIX-pipe (=fifo). When DC triggers UpdateController, it has to check if
# an update is possible, i.e. is has to lock the PSA for the update procedure.
# UpdateController will send back the update-result to DeviceController
# which unlocks the PSA to return to nomal operation.
#
# If UpdateController has been triggered, it checks out a predefined git-
# repository. As a sanity check it makes sure that there are some changes in
# the repository. In case there are no changes, it issues an error message
# to DC and returns to its wait-state.
#
# Otherwise it checks if the changed files are correct (using md5) and if
# the new files are valid for the PSA. If there is some problem, it issues an
# error message to DC, reverts the git-repository to its previous state and
# returns to its wait-state.
#
# Otherwise it makes a backup of the current state of the PSA, and if there
# are opkg-packages to install it runs a dry installation process using opkg.
# In case of error, it issues an error message to DC, deletes the backup,
# reverts # the git-repository to its previous state and returns to its
# wait-state.
#
# Otherwise, it copies all new files to their target locations and in case
# of opkg-packages, it installs the packages.
#
# In case of error it restores the previous state using the backup and
# reinstalls the previous opkg-package(s). It issues an error message to
# DC and returns to its wait-state.
#
# Otherwise the update went well, and it does some cleanup, sends a success
# message to DC and returns to its wait-state.
#
###############################################################################
#
# UPDATE PROCEDURE
#
###############################################################################
update() {
local try_update_count=0
local func="${FUNCNAME[0]}"
# read config parameters
# read_config
log_debug "$func:${LINENO}: fetch/merge updates..."
# Fetch new updates (using git)
while :
do
local repository_is_already_up_to_date=""
if ! fetch_customer_updates repository_is_already_up_to_date; then
if [ "$repository_is_already_up_to_date" = "yes" ]; then
log_error "$func:${LINENO}: $customer_id is up-to-date"\
"-> no files to update -> no psa update"
exit 1
fi
try_updates_count=$((try_updates_count+1))
if [[ "$try_updates_count" -eq 5 ]]; then
log_error "$func:${LINENO}: fetch/merging failed" ; exit 1
fi
sleep 60s
else
# Fetched updates successfully
try_updates_count=0
break
fi
done
# Backup before any updates in case some previous test was wrong
if ! backup_previous_version; then
log_error "$func:${LINENO}: backup failed"
revert_customer_repository ; exit 1
fi
files=$(changed_file_names)
if ! check_md5_for_changed_customer_files $files ; then
log_error "$func:${LINENO}: new customer files wrong"
revert_customer_repository ; exit 1
fi
if grep -qE ".*opkg_commands.*?" <<< $files; then
# read opkg_cmds: each line respresents an opkg-command
readarray opkg_commands < <(cat $opkg_cmds_file)
for opkg_command in "${opkg_commands[@]}"; do
if grep -qE "^\s*[#]+.*?$" <<< $opkg_command; then
continue # found comment line
fi
# FIXME: sollte nicht gebraucht werden
opkg_command=${opkg_command//[$'\r\n\t']/ }
local package=$(printf '%s' "$opkg_command" | awk '{ print $NF }')
local opkg_output=()
if ! exec_opkg_info "$package" opkg_output; then
log_error "$func:${LINENO}: opkg --noaction $opkg_command failed"
revert_customer_repository ; exit 1
fi
# FIXME: sollte am anfang so gesetzt werden
SAVEIFS=$IFS
IFS=$'\n'
if ! check_md5_for_opkg_packages opkg_output; then
log_error "$func:${LINENO}: wrong md5sum for some opkg packages"
revert_customer_repository ; exit 1
fi
IFS=$SAVEIFS
# perform a dry-run and check if everything might work as expected.
# Actually execute the opkg command
if ! exec_opkg $opkg_command; then
log_error "$func:${LINENO}: do_update failed"
fallback_to_previous_version
revert_customer_repository ; exit 1
fi
done
else
log_info "$func:${LINENO}: no opkg commnds to execute"
fi
# Cleanup.
if ! cleanup_previous_version; then
log_error "$func:${LINENO}: cleanup_previous_version failed"
fi
log_info "$func:${LINENO}: success"
exit 0
}
###############################################################################
if [ $# -ne 1 ] ; then
echo "Usage: $0 filename"
exit 1
else
# if [ -z $IFS ]; then
# IFS=$'\n'
# fi
if read_config "$1" ; then
# set -x
if clone_customer_repository $repository_path ; then
update
fi
fi
fi

157
update_psa_helpers Normal file
View File

@ -0,0 +1,157 @@
#!/bin/bash
# if [ ${git_helpers_sourced:-1} = "1" ]; then
# readonly git_helpers_sourced=${BASH_SOURCE[0]}
#else
# return 0
#fi
exec_opkg_command () {
local func="${FUNCNAME[0]}"
log_debug "$func:${LINENO} exec-ing [$*]"
exec {fd}< <($@)
local ps_pid=$! # remember pid of process substitution
local opkg_result=""
while read tmp <&$fd; do
if ! [ -z "$tmp" ]; then
opkg_result="${opkg_result}$tmp"
fi
done
exec {fd}>&- # close fd (i.e. process substitution)
wait $ps_pid # wait for the subshell to finish
printf '%s' "$opkg_result"
}
# Fetch/merge updates from predefined repository using git.
#
fetch_customer_updates() {
local func="${FUNCNAME[0]}"
if ! pull_customer_repository $1; then
return 1
fi
return 0
}
# Check if the fetched/merged files have the correct md5 and are
# valid for the PSA.
#
check_md5_for_changed_customer_files () {
local func="${FUNCNAME[0]}"
#for file in ($1); do
# Check the updates for correct MD5
# log_error "$func:${LINENO}: new customer files wrong"
# revert_customer_repository ; exit 1
# fi
#done
return 0
}
check_md5_for_opkg_packages () {
local func="${FUNCNAME[0]}"
local -n opkg_output_ref=$1
local package=""
local md5sum=""
local filename=""
for line in ${opkg_output_ref[@]}; do
if grep -qE "^\s*Package\s*:.*?$" <<< "$line"; then
package=${line#*:* }
printf 'package=%s\n' "$package"
elif grep -qE "^\s*MD5Sum\s*:.*?$" <<< "$line"; then
md5sum=${line#*:* }
printf 'md5sum=%s\n' "$md5sum"
elif grep -qE "^\s*Filename\s*:.*?$" <<< "$line"; then
filename=${line#*:* }
printf 'filename=%s\n' "$filename"
fi
done
log_debug "$func:${LINENO}: $package | $md5sum | $filename"
return 0
}
# In case the new checked-out files are not correct, revert the git
# repository to its previous state.
#
revert_customer_repository() {
local func="${FUNCNAME[0]}"
return 0
}
# Backup before the update-process.
#
backup_previous_version () {
local func="${FUNCNAME[0]}"
return 0
}
exec_opkg_info () {
local func="${FUNCNAME[0]}"
log_info "$func:${LINENO}: executing $opkg_command"
opkg_result=$(exec_opkg_command "opkg info $1")
# make sure the keywords start with '\n'
opkg_result=$(sed -E -e "s/(^.*)(Package)(.*$)/\n\2\3/g"\
-e "s/(^.*)(Version)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Depends)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Status)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Architecture)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Maintainer)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(MD5Sum)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Filename)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Source)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Description)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Installed-Size)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Section)(.*$)/\1\n\2\3/g"\
-e "s/(^.*[^-])(Size)(.*$)/\1\n\2\3/g"\
-e "s/(^.*)(Installed-Time)(.*$)/\1\n\2\3/g" <<< "$opkg_result")
local -n output_ref=$2
readarray -d $'\n' output_ref < <(printf '%s' "$opkg_result")
if [ $? -ne 0 ]; then
log_error "$func:${LINENO}: readarray finished with error"
return 1
fi
log_info "$func:${LINENO}: ... done"
log_info "$func:${LINENO}: opkg_result=${output_ref[@]}"
return 0
}
# Try to install new opkg-packages (in case the are some
# in the new git-checkout).
#
exec_opkg_no_action() {
local func="${FUNCNAME[0]}"
local opkg_command_no_action="opkg --noaction $1"
log_info "$func:${LINENO}: executing $opkg_command_no_action"
return 0
}
# Install the new packages using opkg.
#
exec_opkg () {
local func="${FUNCNAME[0]}"
return 0
}
# In case there was some error, re-install the previous package(s)
# and use the backup to restore any conf/ini-files.
#
fallback_to_previous_version() {
local func="${FUNCNAME[0]}"
return 0;
}
# If all went well, then execute all necessary cleanup steps.
#
cleanup_previous_version() {
local func="${FUNCNAME[0]}"
return 0
}