[U-Boot] [RFC] Safe Linux Updater

Alexandre Dilly
2013-06-18T09:05:48Z

Dear Wolfgang, Mats and Stefano,

Thanks for your answers,
> In message <ED3E0BCACD909541BA94A34C4A164D4C4FC9145C@post.tritech.se>
> you wrote:
> >
> > I haven't seen the scripts of Alexandre but it sounds something like
> > what
> > we have already implemented.
>
> I haven't seen the scripts either, but this is something that has
> been implemented many times before, with many variations depending on
> specific project needs. But as I mentioned before, most users do not
> push such scripts into mainline...
>
Yes, this kind of system has been implemented many times before but, we
can't found any examples in u-boot source, so the idea was to give a
generic example to gain time in updater development...

> > > Please note that this is a feature standardized for example in
> > > the
> > > Open Source Development Labs Carrier Grade Linux Requirements
> > > Definition, which says something like: "CGL shall provide
> > > support
> > > for detecting a repeating reboot cycle due to recurring
> > > failures
> > > and will go to an offline state if this occurs."
> >
> > As I read Alexandre, the aim is to revert to a previous functional
> > image,
>
> Yes, of course. And the boot counter is the mechanism that will
> decide when to do that.
>
> > not to go to an offline state.
>
> Indeed. U-Boots boot counter will select an alternative boot command
> in this case - you can use this to hard hang or power off the board
> and so implement strictly CGL conformant behaviour, or you can do
> something else like fall back to the previous version.
>
U-boots boot counter is not persistent which implies we can't handle
some error cases like power fail. That's why I suggested to use
environment to store boot counter with redundant option to be power safe.

> > > Normally you want to avoid all erase / write operations to
> > > the boot loader and it's private data structures in the process
> > > of
> > > a normal reboot / reset.
> >
> > But a failing boot is not a normal boot. This should only occur
> > when an
> > update fails. After a maximum number of failing boots, the old
> > functional
>
> Alexandre wrote about a boot counter; he did not mention that he
> would
> update this only on failed boot attempts.
>
> Also, in my experience one should be especially careful when
> something
> fails, and in such a situation I would all the more restrain from
> messing with the environment if it can be avoided (and here it's
> trivial to avoid).
>
> > image is used and there is no need to update the counter any more.
>
> Did you have a look at the current implementation of the boot counter
> for the systems where it is supported? Yes, we even do have a system
> where the boot counter is stored in an environment variable (due to
> hardware restrictions and the expectation that the system will
> normally not need to be rebooted at all), but normally is can be
> easily avoided to meddle with the environment for this functionality.
>
In fact, what Mats explain was my idea: the system updates boot counter
only when it detects a partition marked as "updated". When Linux has
booted, it does some specific verifications and then mark the partition
as "good" or as "bad". So, boot counter is not updated any more until a
new update is installed on system. So, like said Mats, count update only
occurs when an update fails and then it doesn't cause excessive memory
wear, because we have a limit like 3 for boot attempts.

> I find that the proposal does not scale well. Having partitions on a
> disk / SDCARd is a case, but we have several different way to boot.
> Think about kernel / rootfs into UBI or UBIFS, or saved as raw data in
> other kind of storages (NOR, SPI,..). Because we are talking about the
> feature "updating", this should be abstracted from the specific case to
> be generalized in U-Boot.
>
About partition, since the script uses an environment variable to store boot
command for each partition, it can be a disk partition or a UBI partition or
a raw memory area containing kernel or rootfs which will be loaded with boot
command.

To illustrate all ideas, I join to this mail the both scripts. These scripts
are definitively not final versions!

U-boot script:
diff --git a/uboot_script b/uboot_script
new file mode 100644
index 0000000..b189643
--- /dev/null
+++ b/uboot_script
@@ -0,0 +1,64 @@
+setenv tmp_update
+if test "z$force_rescue" != "z"; then
+ echo "Boot on rescue partition..."
+ run part_r_cmd
+ echo "System cannot boot!"
+ exit 1
+fi
+if test "z$max_attempts" = "z"; then
+ echo "Set maximum boot attempts to default (=3)"
+ setenv max_attempts 3
+fi
+for i in $boot_seq;
+do
+ setenv tmp setenv tmp \$part_${i}_flag
+ run tmp
+ if test "z$tmp" != "z"; then
+ if test $tmp = "OK" -o $tmp = "UPDATE"; then
+ if test $tmp = "UPDATE"; then
+ setenv tmp_update 1
+ echo "Partition $i is an update"
+ fi
+ if test "z$tmp_update" != "z"; then
+ setenv tmp setenv tmp \$part_${i}_count
+ run tmp
+ if test "z$tmp" != "z"; then
+ echo "Try to boot on partition $i..."
+ else
+ echo "Reset counter and try to boot on partition $i..."
+ setenv "part_${i}_count" 0
+ setenv tmp 0
+ fi
+ while test $tmp -lt $max_attempts;
+ do
+ setexpr tmp $tmp + 1
+ echo " -> Attempt $tmp on partition $i"
+ setenv "part_${i}_count" $tmp
+ setenv tmp
+ setenv tmp_update
+ saveenv
+ run part_${i}_cmd
+ echo " -> Boot failed!"
+ done
+ if test $tmp -eq $max_attempts; then
+ setenv "part_${i}_count" 254
+ setenv tmp
+ setenv tmp_update
+ saveenv
+ fi
+ echo "Partition $i failed to boot!"
+ else
+ echo "Boot on partition $i..."
+ run part_${i}_cmd
+ echo " -> Boot failed on partition $i!"
+ fi
+ fi
+ else
+ echo "No flag for partition $i"
+ fi
+done
+echo "Boot on partitions failed! Boot on rescue partition..."
+run part_r_cmd
+echo "System cannot boot!"
+exit 1
+

Linux script:
diff --git a/smart_boot.sh b/smart_boot.sh
new file mode 100755
index 0000000..a0561ef
--- /dev/null
+++ b/smart_boot.sh
@@ -0,0 +1,523 @@
+#!/bin/sh
+
+# Script Configuration
+SETENV_CMD="fw_setenv" # Path to fw_setenv binary
+PRINTENV_CMD="fw_printenv" # Path to fw_printenv binary
+TMP_FILE="/tmp/smart_boot.tmp" # Temporary file used for env writting
+# Entry name in env
+PART_FLAG_NAME="part_\${i}_flag" # Partition 'i' flag
+PART_CMD_NAME="part_\${i}_cmd" # Boot command for partition 'i'
+PART_COUNT_NAME="part_\${i}_count" # Boot ttempts number for partition 'i'
+PART_TEXT_NAME="part_\${i}_text" # Partition 'i' description text
+PART_NB_NAME="part_nb" # Total partition number
+BOOT_SEQ_NAME="boot_seq" # Boot sequence (ordrer = "first second ... last")
+FORCE_RESCUE_NAME="force_rescue" # Boot on rescue flag
+MAX_ATTEMPT_NAME="max_attempts" # Number of attempts before switching to previous partition
+BOOT_PART_NAME="boot_part" # Name of param appended to linux boot command line. This param contain booted partition index
+
+# Print usage
+print_usage () {
+ echo "Usage: ./smart_boot TASK PARMAS" 1>&2
+ echo "List of tasks (=TASK) available:" 1>&2
+ echo " print: Print boot configuration." 1>&2
+ echo " allocate: Allocate an empty/bad partition or " 1>&2
+ echo " oldest partition to install an update." 1>&2
+ echo " update: Mark the allocated parititon with UPDATED flag. " 1>&2
+ echo " Needs to call 'allocate' before." 1>&2
+ echo " validate: Validate a partition with UPDATED flag." 1>&2
+ echo " invalidate: Invalidate a partition with UPDATED flag." 1>&2
+ echo " partition: Return theorical booted partition." 1>&2
+ echo " real-partition: Return real booted partition (from linux cmdline)." 1>&2
+ echo " clean: Corrects boot configuration." 1>&2
+ echo " force-rescue: Force system to boot on rescue partition." 1>&2
+ echo " unforce-rescue: Unforce system to boot on rescue partition." 1>&2
+ echo " status: Return a status number of boot case." 1>&2
+ echo "Manual informations:" 1>&2
+ echo " get-flag PART: Get flag for partition PART." 1>&2
+ echo " get-text PART: Get text description for partition PART." 1>&2
+ echo " get-count PART: Get boot attempt count for partition PART." 1>&2
+ echo " get-boot-seq: Get boot sequence." 1>&2
+ echo "Manual modification:" 1>&2
+ echo " set-flag PART FLAG: Set flag with FLAG for partition PART." 1>&2
+ echo " set-text PART TXT: Set text description with TXT for partition PART." 1>&2
+ echo " set-count PART CNT: Set boot attempt count to CNT for partition PART." 1>&2
+ echo " set-boot-seq SEQ: Set boot sequence with SEQ." 1>&2
+ return
+}
+
+# Verify number of arguments and print usage if lower than 1
+if [ $# -lt 1 ]; then
+ print_usage
+ exit 1
+fi
+
+# Get arguments
+task=$1
+
+# Get number of partition
+part_nb=$($PRINTENV_CMD -n $PART_NB_NAME 2>/dev/null)
+if [ $part_nb -lt 2 ]; then
+ echo "Bad number of partition: you need at least 2 partitions!" 1>&2
+ exit 1
+fi
+
+# Count entries in boot_seq
+boot_seq_count=0
+boot_seq="$($PRINTENV_CMD -n $BOOT_SEQ_NAME 2>/dev/null)"
+if [ $? -eq 0 ]; then
+ boot_seq_count=$(echo $boot_seq | wc -w)
+fi
+
+# Get maximum boot attempts
+nb_attempt="$($PRINTENV_CMD -n $MAX_ATTEMPT_NAME 2>/dev/null)"
+if [ $? -ne 0 ]; then
+ nb_attempt=3
+ echo "No maximum boot attempts in environment: set with default value (3)!" 1>&2
+fi
+
+# Switch case for each tasks (=TASK)
+case $task in
+ # Print boot configuration
+ print )
+ # Print flags for each partition
+ for i in $(seq 1 $part_nb)
+ do
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ echo "Partition $i: $flag (boot attempts: $count)"
+ txt=$($PRINTENV_CMD -n $(eval echo $PART_TEXT_NAME) 2>/dev/null)
+ if [ $? -eq 0 ]; then
+ echo " ($txt)"
+ fi
+ done
+ # Print boot sequence
+ echo "Boot sequence: $($PRINTENV_CMD -n $(eval echo $BOOT_SEQ_NAME) 2>/dev/null)"
+ ;;
+ # Allocate an empty/bad partition or oldest partition to install an update.
+ allocate )
+ i=0
+ # If all partitions are used, pick the oldest
+ if [ $boot_seq_count -eq $part_nb ]; then
+ # Verify flag of first partition
+ i=${boot_seq%% *}
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ "$flag" = "OK" ]; then
+ # Get last partition in boot seq
+ i=$(echo $boot_seq | awk '{print $'$part_nb'}')
+ fi
+ # Else search for an allocated partition or a partition unavailable in boot sequence
+ else
+ part=0
+ for i in $(seq 1 $part_nb)
+ do
+ #If partition isn't in boot sequenc
+ if [ "${boot_seq/$i/}" = "$boot_seq" ]; then
+ # Get partition flag
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $flag = "LOCK" ]; then
+ echo "Partition $i is already allocated!" 1>&2
+ echo $i
+ exit 0
+ else
+ part=$i
+ break
+ fi
+ fi
+ done
+ i=$part
+ fi
+
+ # Change flag of partition with 'LOCK' and remove partition from boot sequence
+ if [ $i -gt 0 ]; then
+ echo "$(eval echo $PART_FLAG_NAME) LOCK" > $TMP_FILE
+ echo "$BOOT_SEQ_NAME ${boot_seq/$i/}" >> $TMP_FILE
+ $SETENV_CMD -s $TMP_FILE
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!"
+ exit 1
+ fi
+ echo "Partition $i has been allocated." 1>&2
+ echo $i
+ else
+ echo "Can't allocate a parititon!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Mark the allocated parititon with UPDATED flag. Needs to call 'allocate' before.
+ update )
+ # Search allocated partition
+ part=0
+ for i in $(seq 1 $part_nb)
+ do
+ #If partition isn't in boot sequenc
+ if [ "${boot_seq/$i/}" = "$boot_seq" ]; then
+ # Get partition flag
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ "z$flag" != "z" -a $flag = "LOCK" ]; then
+ part=$i
+ break
+ fi
+ fi
+ done
+ if [ $part -gt 0 ]; then
+ i=$part
+ # Set partition flag to UPDATE and add partition to boot sequence
+ echo "$(eval echo $PART_FLAG_NAME) UPDATE" > $TMP_FILE
+ echo "$BOOT_SEQ_NAME $i $boot_seq" >> $TMP_FILE
+ if [ $# -gt 1 ]; then
+ echo "$(eval echo $PART_TEXT_NAME) $2" >> $TMP_FILE
+ else
+ echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE
+ fi
+ for i in $(seq 1 $part_nb)
+ do
+ echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE
+ done
+ $SETENV_CMD -s $TMP_FILE
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!"
+ exit 1
+ fi
+ echo "Partition $part has been marked as an update." 1>&2
+ echo $part
+ else
+ echo "Please allocate a partition before calling 'update'!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Validate a partition with UPDATED flag.
+ validate )
+ # Get flag of first partition of boot sequence
+ if [ $boot_seq_count -lt 1 ]; then
+ echo "No partitions in boot sequence!" 1>&2
+ exit 1
+ fi
+ i=${boot_seq%% *}
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $? -eq 0 -a $flag = "UPDATE" ]; then
+ # Verify number of attempts
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ if [ $? -ne 0 -o $count -gt $nb_attempt ]; then
+ echo "Partition $i has failed to boot: you can't mark it as good! Please use invalidate or clean." 1>&2
+ exit 1
+ fi
+ # Set flag to OK
+ $SETENV_CMD "$(eval echo $PART_FLAG_NAME)" "OK"
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!"
+ exit 1
+ fi
+ echo "Partition $i has been marked as ok. Now system will boot on this partition." 1>&2
+ echo $i
+ else
+ echo "The first partition is not an update!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Invalidate a partition with UPDATED flag.
+ invalidate )
+ # Get flag of first partition of boot sequence
+ if [ $boot_seq_count -lt 1 ]; then
+ echo "No partitions in boot sequence!" 1>&2
+ exit 1
+ fi
+ i=${boot_seq%% *}
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $? -eq 0 -a $flag = "UPDATE" ]; then
+ # Set flag to BAD and remove partition from boot sequence
+ echo "$(eval echo $PART_FLAG_NAME) BAD" > $TMP_FILE
+ echo "$BOOT_SEQ_NAME ${boot_seq#* }" >> $TMP_FILE
+ $SETENV_CMD -s $TMP_FILE
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!"
+ exit 1
+ fi
+ echo "Partition $i has been marked as bad." 1>&2
+ echo $i
+ else
+ echo "The first partition is not an update!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Return theorical booted partition.
+ partition )
+ update=0
+ force=$($PRINTENV_CMD -n $(eval echo $FORCE_RESCUE_NAME) 2>/dev/null)
+ if [ $? -eq 0 ]; then
+ echo "Booted on rescue partition (forced)." 1>&2
+ echo 0
+ exit 0
+ fi
+ for i in $boot_seq
+ do
+ # Get flag and boot attempt count for partition
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ if [ $count -le $nb_attempt ]; then
+ echo "Booted on partition $i" 1>&2
+ echo $i
+ exit 0
+ fi
+ done
+ echo "Booted on rescue partition" 1>&2
+ echo 0
+ ;;
+ # Return real booted partition.
+ real-partition )
+ boot_part="$(sed -n 's|.*'$BOOT_PART_NAME'=\([^ ]\+\).*|\1|p' < /proc/cmdline)"
+ if [ "z$boot_part" != "z" ]; then
+ if [ $boot_part = "r" ]; then
+ echo "Booted on rescue partition" 1>&2
+ echo 0
+ else
+ echo "Booted on partition $boot_part" 1>&2
+ echo $boot_part
+ fi
+ else
+ echo "No $BOOT_PART_NAME in kernel command line!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Fix on boot configuration.
+ clean )
+ update=0
+ echo -n "" > $TMP_FILE
+ for i in $(seq 1 $part_nb)
+ do
+ #If partition is in boot sequenc
+ if [ "${boot_seq/$i/}" != "$boot_seq" ]; then
+ # Verify flag
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $? -ne 0 ]; then
+ # Remove partition from boot sequence and set flag to NONE
+ echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE
+ echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE
+ boot_seq=${boot_seq/$i/}
+ else
+ # Verify if it is an update or a good partition
+ if [ "$flag" != "OK" -a "$flag" != "UPDATE" ]; then
+ # Set partition as NONE and remove it from boot sequence
+ echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE
+ echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE
+ boot_seq=${boot_seq/$i/}
+ else
+ # Verify if it is an update parition
+ if [ $flag = "UPDATE" ]; then
+ update=1
+ fi
+ # If we are in an update case, verify boot attempts
+ if [ $update -eq 1 ]; then
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ if [ $? -ne 0 -o $count -gt $nb_attempt ]; then
+ # Set partition as BAD and remove it from boot sequence
+ echo "$(eval echo $PART_FLAG_NAME) BAD" >> $TMP_FILE
+ boot_seq=${boot_seq/$i/}
+ else
+ #Set partition as OK
+ if [ $count -gt 0 ]; then
+ echo "$(eval echo $PART_FLAG_NAME) OK" >> $TMP_FILE
+ elif [ "$flag" = "UPDATE" ]; then
+ echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE
+ echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE
+ boot_seq=${boot_seq/$i/}
+ fi
+ fi
+ fi
+ fi
+ # Reset boot attempt count
+ echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE
+ fi
+ else
+ # Set flag as NONE and boot attempt to 0
+ echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE
+ echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE
+ echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE
+ fi
+ done
+ echo "$BOOT_SEQ_NAME $boot_seq" >> $TMP_FILE
+ $SETENV_CMD -s $TMP_FILE
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Force to boot on rescue partition.
+ force-rescue )
+ $SETENV_CMD $FORCE_RESCUE_NAME "1"
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Unforce boot on rescue partition.
+ unforce-rescue )
+ $SETENV_CMD $FORCE_RESCUE_NAME ""
+ if [ $? -ne 0 ]; then
+ echo "Failed to write in Environment!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Return a number for boot status
+ status )
+ echo "Code meanings:" 1>&2
+ echo " -> 0: Boot on normal partition." 1>&2
+ echo " -> 1: Boot on updated partition (needs to be validated)." 1>&2
+ echo " -> 2: Boot on normal partition after boot failure on updated partition." 1>&2
+ echo " -> 3: Boot on rescue partition after multiple boot failure." 1>&2
+ echo " -> 4: Boot on rescue partition." 1>&2
+ echo " -> 5: Forced boot on rescue partition." 1>&2
+ if $PRINTENV_CMD "$FORCE_RESCUE_NAME" 2>/dev/null; then
+ echo "Forced boot on rescue partition." 1>&2
+ echo "5"
+ else
+ if [ $boot_seq_count -eq 0 ]; then
+ echo "Boot on rescue partition." 1>&2
+ echo "4"
+ else
+ update=0
+ for i in $boot_seq
+ do
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $? -eq 0 -a $flag = "UPDATE" ]; then
+ update=1
+ fi
+ if [ $update -eq 1 ]; then
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ if [ $? -eq 0 -a $count -le $nb_attempt ]; then
+ if [ $flag = "OK" ]; then
+ echo "Boot on normal partition after boot failure on updated partition." 1>&2
+ echo "2"
+ exit 0
+ else
+ echo "Boot on updated partition (needs to be validated)." 1>&2
+ echo "1"
+ exit 0
+ fi
+ fi
+ else
+ echo "Boot on normal partition." 1>&2
+ echo "0"
+ exit 0
+ fi
+ done
+ echo "Boot on rescue partition after multiple boot failure." 1>&2
+ echo "3"
+ fi
+ fi
+ ;;
+ # Get partition flag
+ get-flag )
+ if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null)
+ if [ $? -eq 0 ]; then
+ echo "$flag"
+ else
+ echo "Flag partition not defined!" 1>&2
+ exit 1
+ fi
+ else
+ echo "Please specify a correct partition!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Get partition text description
+ get-text )
+ if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ text=$($PRINTENV_CMD -n $(eval echo $PART_TEXT_NAME) 2>/dev/null)
+ if [ $? -eq 0 ]; then
+ echo "$text"
+ else
+ echo "Flag partition not defined!" 1>&2
+ exit 1
+ fi
+ else
+ echo "Please specify a correct partition!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Get boot attempt count of a partition
+ get-count )
+ if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null)
+ if [ $? -eq 0 ]; then
+ echo "$count"
+ else
+ echo "Flag partition not defined!" 1>&2
+ exit 1
+ fi
+ else
+ echo "Please specify a correct partition!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Get boot sequence
+ get-boot-seq )
+ echo "$($PRINTENV_CMD -n $BOOT_SEQ_NAME 2>/dev/null)"
+ ;;
+ # Set a partition flag
+ set-flag )
+ if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ $SETENV_CMD "$(eval echo $PART_FLAG_NAME)" "$3"
+ echo "Flag of partition $i updated!" 1>&2
+ else
+ echo "Please specify a partition and a flag to set!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Set a partition text description
+ set-text )
+ if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ $SETENV_CMD "$(eval echo $PART_TEXT_NAME)" "$3"
+ echo "Text description of partition $i updated!" 1>&2
+ else
+ echo "Please specify a partition and a text to set!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Set boot attempt count of a partition
+ set-count )
+ if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then
+ i=$2
+ $SETENV_CMD "$(eval echo $PART_COUNT_NAME)" "$3"
+ echo "Boot attempt count of partition $i updated!" 1>&2
+ else
+ echo "Please specify a partition and a count to set!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Set boot sequence
+ set-boot-seq )
+ if [ $# -ge 2 ]; then
+ # Verify sequence
+ for i in $2
+ do
+ if [ $i -le 0 -o $i -gt $part_nb ]; then
+ echo "Bad boot sequence!" 1>&2
+ exit 1
+ fi
+ done
+ $SETENV_CMD "$BOOT_SEQ_NAME" "$2"
+ echo "Boot sequence changed." 1>&2
+ else
+ echo "Please specify a boot sequence!" 1>&2
+ exit 1
+ fi
+ ;;
+ # Print usage message.
+ help )
+ print_usage
+ ;;
+ # Unknown task
+ * )
+ print_usage
+ exit 1
+ ;;
+esac
+
+exit 0
+

Best regards,

Alexandre Dilly