summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/overview.org20
-rw-r--r--files/common/etc/fsm/update/common.sh36
-rw-r--r--files/common/etc/fsm/update/initial_state1
-rwxr-xr-xfiles/common/etc/fsm/update/trans/default.enter5
-rwxr-xr-xfiles/common/etc/fsm/update/trans/scheduled-applying.trans6
-rwxr-xr-xfiles/common/etc/fsm/update/trans/scheduled.enter9
-rwxr-xr-xfiles/common/etc/fsm/update/trans/scheduled.leave9
-rwxr-xr-xfiles/common/etc/fsm/update/watch/default72
8 files changed, 148 insertions, 10 deletions
diff --git a/doc/overview.org b/doc/overview.org
index 019638d..78bf230 100644
--- a/doc/overview.org
+++ b/doc/overview.org
@@ -24,7 +24,7 @@
* State machines (FSMs)
State machines are implemented using the /sbin/fsm script (see
below).
-** Network
+** inetable
Controls the different network states that result of the local
availability of internet connection and the state of the cloud.
@@ -65,13 +65,13 @@ digraph dsd {
- all internet traffic is redirected to a local httpd, yelling the
network status and explaining FFJ
-** Update
+** update
Implements all-or-nothing update of nodes (e.g. if the network
protocol changes incompatibly). Synchronized via p2ptable
firmware-versions with the fields
- machine_id
- - current firmware (sha256)
- - target firmware (sha256); empty if no update shall be performed
+ - current firmware (some human readable version string)
+ - SHA256 of target firmware; empty if no update shall be performed
- time target: set by admin to time when update shall happen
- acknowledge time: set by device to time target once ready for an
upgrade
@@ -81,9 +81,9 @@ digraph dsd {
#+begin_dot FSM_Update.png -Tpng
digraph {
- Idle -> Ready;
- Ready ->{ Scheduled; Idle };
- Scheduled ->{Applying; Idle };
+ Idle -> Ready;
+ Ready ->{Idle; Scheduled}
+ Scheduled ->{Idle; Scheduled; Applying}
}
#+end_dot
*** Idle
@@ -98,8 +98,8 @@ digraph {
conditions hold:
1. target firmware, update time target and acknowledge update time
are empty
- 2. current time > time target == acknowledge time; And target
- firmware points to a new version that is locally stored an
+ 2. time target == acknowledge time; And target
+ firmware points to a new version that is locally stored and
verified
Once this state is reached the update is performed.
@@ -120,7 +120,7 @@ digraph {
responds, we are online
- returns connectivity status
- TODO: ping multiple hosts in parallel
-** Finite state machine
+** Finite state machines
FSMs are implemented using
- /sbin/fsm :: a script to monitor and change the state:
- fsm watch <name> :: check whether a state change shall occur
diff --git a/files/common/etc/fsm/update/common.sh b/files/common/etc/fsm/update/common.sh
new file mode 100644
index 0000000..8218dd5
--- /dev/null
+++ b/files/common/etc/fsm/update/common.sh
@@ -0,0 +1,36 @@
+Tbl=/tmp/p2ptbl/update
+TblIf=br-mesh
+FwDir=/tmp/firmware
+
+# updates global state using current internal state; implicit
+# definition of the table format
+GS_update () {
+ p2ptbl update $Tbl "$NodeId" "$CurFw\t$TargetFw\t$TargetTime\t$AckTime" $TblIf
+}
+
+# assemble internal state
+CurState=$1
+CurTime=$(date +%s)
+NodeId=$(cat /proc/sys/kernel/random/boot_id) # TODO: replace with stable machine id
+CurFw="$(cat /etc/firmware)"
+[ -n "$NodeId" -a -n "$CurFw" ]
+
+# get current global state from p2ptable
+p2ptbl init $Tbl
+GS=$(p2ptbl get $Tbl $NodeId)
+if [ -n "$GS" ]; then
+ GSCurFw=$( echo "$GS" | cut -f1)
+ TargetFw=$( echo "$GS" | cut -f2)
+ TargetTime=$(echo "$GS" | cut -f3)
+ AckTime=$( echo "$GS" | cut -f4)
+ # update stale firmware entries .. should only happen after manual
+ # edit of /etc/firmware
+ [ "$CurFw" == "$GSCurFw" ] || GS_update
+else
+ # no entry exists -> create one
+ TargetFw=""
+ TargetTime=""
+ AckTime=""
+ GS_update
+fi
+FwDst=$FwDir/$TargetFw
diff --git a/files/common/etc/fsm/update/initial_state b/files/common/etc/fsm/update/initial_state
new file mode 100644
index 0000000..5a5e41c
--- /dev/null
+++ b/files/common/etc/fsm/update/initial_state
@@ -0,0 +1 @@
+idle
diff --git a/files/common/etc/fsm/update/trans/default.enter b/files/common/etc/fsm/update/trans/default.enter
new file mode 100755
index 0000000..ffdb026
--- /dev/null
+++ b/files/common/etc/fsm/update/trans/default.enter
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+. ../common.sh
+
+CurState=$2
+GS_update
diff --git a/files/common/etc/fsm/update/trans/scheduled-applying.trans b/files/common/etc/fsm/update/trans/scheduled-applying.trans
new file mode 100755
index 0000000..928d9eb
--- /dev/null
+++ b/files/common/etc/fsm/update/trans/scheduled-applying.trans
@@ -0,0 +1,6 @@
+#!/bin/sh -e
+. ../common.sh
+
+(echo "I would have called: sysupgrade $FwDst"
+ date
+ echo) > /tmp/updatophilie \ No newline at end of file
diff --git a/files/common/etc/fsm/update/trans/scheduled.enter b/files/common/etc/fsm/update/trans/scheduled.enter
new file mode 100755
index 0000000..7488570
--- /dev/null
+++ b/files/common/etc/fsm/update/trans/scheduled.enter
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+. ../common.sh
+
+AckTime=$TargetTime
+CurState=$2
+GS_update
+
+# TODO: may be increase gossip frequency to allow faster updates
+# despite packet loss and queue overflows in hbbpd \ No newline at end of file
diff --git a/files/common/etc/fsm/update/trans/scheduled.leave b/files/common/etc/fsm/update/trans/scheduled.leave
new file mode 100755
index 0000000..69826fe
--- /dev/null
+++ b/files/common/etc/fsm/update/trans/scheduled.leave
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+. ../common.sh
+
+AckTime=""
+
+# disable gossip during update - the new state is broadcasted during
+# *.enter immediately
+TblIf=
+GS_update
diff --git a/files/common/etc/fsm/update/watch/default b/files/common/etc/fsm/update/watch/default
new file mode 100755
index 0000000..a6858af
--- /dev/null
+++ b/files/common/etc/fsm/update/watch/default
@@ -0,0 +1,72 @@
+#!/bin/sh -e
+set -x
+
+. ../common.sh
+
+# announce a transition and terminate
+trans () {
+ echo $1
+ exit
+}
+
+# is the firmware file available?
+checkFw () {
+ [ -n "$TargetFw" -a -f "$FwDst" ] || trans idle
+}
+
+checkFwHash () {
+ [ "$(sha256sum <$FwDst 2>/dev/null | cut -f1 -d' ')" == "$TargetFw" ] || trans idle
+}
+
+# is target time passed but not set
+checkTime () {
+ [ $CurTime -lt "$TargetTime" -o "$AckTime" -eq "$TargetTime" ] 2>/dev/null \
+ || trans ready
+}
+
+# check all-or-nothing-invariant for whole update state table
+checkPeerState() {
+ p2ptbl show $Tbl \
+ | (
+ IFS=$'\t'
+ while read OId OCurFw OTargetFw OTargetTime OAckTime; do
+ [ -z "$OTargetFw" -a -z "$OTargetTime" -a -z "$OAckTime" ] && continue
+ [ -n "$OTargetFw" -a "$OTargetTime" -eq "$OAckTime" ] && continue
+ exit 1
+ done ) || trans ready
+}
+
+
+case $CurState in
+ idle)
+ checkFw
+ checkFwHash
+ # ^^ check hash of firmware file once if the hash is correct;
+ # do not optimize for the rare case of a wrong hash or
+ # continuously failing updates
+ trans ready
+ ;;
+ ready)
+ checkFw
+ checkTime
+ [ "$TargetTime" -gt $CurTime ] 2>/dev/null || trans ready
+ trans scheduled
+ ;;
+ scheduled)
+ checkFw
+ checkTime
+ [ $CurTime -gt "$TargetTime" ] || trans scheduled
+ checkPeerState
+ trans applying
+ ;;
+ *)
+ # anything else is illegal; especially the state "applying"
+ # must not be reached: during transition toward it the
+ # firmware is flashed and the router rebooted
+ echo "illegal state $CurState" >&2
+ exit 1
+ ;;
+esac
+
+# default and if any case falls through
+trans idle \ No newline at end of file
contact: Jan Huwald // Impressum