How to access the image "device" attribute from a Lua preinst hook?

90 views
Skip to first unread message

Michael Opdenacker

unread,
Aug 4, 2025, 3:01:17 PMAug 4
to swup...@googlegroups.com, michael.o...@rootcommit.com
Greetings,

A question from someone trying to write his first preinstall hook in Lua...

I'm trying to implement a Lua "preinst" hook that checks that the
swupdate command is not trying to update the current root filesystem
(that's allowed!).

I managed to get the current root filesystem:

local root = swupdate.getroot()
if root.path ~= nil then
    print(root["path"])
end

However, I'm struggling to find how to access the target device that
swupdate was asked to update ("device" property of the selected image).

Am I really supposed to create a new Lua image handler and register it
to access to the image properties? And how to call it explicitly from my
"preinst" hook? I see the swupdate.call_handler() function, but it needs
an image parameter that I don't have in this context.

I'd love to just get a reference to an image table and access its
"device" property. Why isn't there a swupdate.get_image() function?

Thank you in advance for your hints. I guess my questions reveal my very
limited understanding of Lua and swupdate's internals so far :-}
Michael.

--
Michael Opdenacker
Root Commit
Yocto Project and OpenEmbedded Training course - Learn by doing:
https://rootcommit.com/training/yocto/

Stefano Babic

unread,
Aug 4, 2025, 4:15:46 PMAug 4
to Michael Opdenacker, swup...@googlegroups.com
Hi Michael,

On 04.08.25 20:25, Michael Opdenacker wrote:
> Greetings,
>
> A question from someone trying to write his first preinstall hook in Lua...
>

ok

> I'm trying to implement a Lua "preinst" hook that checks that the
> swupdate command is not trying to update the current root filesystem
> (that's allowed!).
>
> I managed to get the current root filesystem:
>
> local root = swupdate.getroot()
> if root.path ~= nil then
>     print(root["path"])
> end
>
> However, I'm struggling to find how to access the target device that
> swupdate was asked to update ("device" property of the selected image).
>
> Am I really supposed to create a new Lua image handler and register it
> to access to the image properties?

No. An image handler is supposed to install an artifact of a new type.

> And how to call it explicitly from my
> "preinst" hook? I see the swupdate.call_handler() function, but it needs
> an image parameter that I don't have in this context.
>
> I'd love to just get a reference to an image table and access its
> "device" property. Why isn't there a swupdate.get_image() function?

There is...

The hook runs inside the parser and it is added to sw-description. As
pseudo-code:

software =
{
version = "@@DISTRO_VERSION@@";

embedded-script ="

......

function find_where_to_install(image)
t = swupdate.getroot()
swupdate.trace('Root Type =' .. t.type)
swupdate.trace('Root =' .. t.value)

if (string.match(t.value, <value to compare>) then
image.device = <set to standby, A value>
else
image.device = <set to standby, B value>
end

return true, image
end";


.......

images (
{
filename = ....;
.....
hook = "find_where_to_install";
}
);



In fact, the hook is receiving an image table with all attributes set in
sw-description. The hook is able to access them, and to change according
to own logic. So you have access to image.filename, image.device,..etc.

>
> Thank you in advance for your hints. I guess my questions reveal my very
> limited understanding of Lua and swupdate's internals so far :-}

Best regards,
Stefano

Khem Raj

unread,
Aug 4, 2025, 4:17:20 PMAug 4
to Michael Opdenacker, swup...@googlegroups.com
On Mon, Aug 4, 2025 at 12:01 PM Michael Opdenacker
<michael.o...@rootcommit.com> wrote:
>
> Greetings,
>
> A question from someone trying to write his first preinstall hook in Lua...
>
> I'm trying to implement a Lua "preinst" hook that checks that the
> swupdate command is not trying to update the current root filesystem
> (that's allowed!).
>
> I managed to get the current root filesystem:
>
> local root = swupdate.getroot()
> if root.path ~= nil then
> print(root["path"])
> end
>
> However, I'm struggling to find how to access the target device that
> swupdate was asked to update ("device" property of the selected image).
>

I think you can use image.device

> Am I really supposed to create a new Lua image handler and register it
> to access to the image properties? And how to call it explicitly from my
> "preinst" hook? I see the swupdate.call_handler() function, but it needs
> an image parameter that I don't have in this context.
>
> I'd love to just get a reference to an image table and access its
> "device" property. Why isn't there a swupdate.get_image() function?
>
> Thank you in advance for your hints. I guess my questions reveal my very
> limited understanding of Lua and swupdate's internals so far :-}
> Michael.
>
> --
> Michael Opdenacker
> Root Commit
> Yocto Project and OpenEmbedded Training course - Learn by doing:
> https://rootcommit.com/training/yocto/
>
> --
> You received this message because you are subscribed to the Google Groups "swupdate" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to swupdate+u...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/swupdate/a301590c-39b6-4771-8a88-6b2a97404f82%40rootcommit.com.

Michael Opdenacker

unread,
Aug 9, 2025, 10:16:05 AMAug 9
to Stefano Babic, michael.o...@rootcommit.com, swup...@googlegroups.com
Hi Stefano

Thanks for your help and advice!
I implemented just that, and it works as you said:

    embedded-script = "
function find_where_to_install(image)
        current_root = swupdate.getroot().value
        swupdate.trace(\"Root =\" .. current_root)

        if current_root == \"/dev/mmcblk0p1\" then
                image.device = \"/dev/mmcblk0p2\"
        elseif current_root == \"/dev/mmcblk0p2\" then
                image.device = \"/dev/mmcblk0p1\"
        else
                swupdate.error(\"Could not identify an expected root
filesystem, aborting.\")
                return false, nil
        end
        return true, image
end
";

For people reading this, the best reference is here:
https://sbabic.github.io/swupdate/sw-description.html#embedded-script

So, this works great to fix the target device, but if swupdate was
passed "-e stable, copy1" instead of "-e stable, copy2", I still get the
bootenv settings from "copy1", while I'd want all the settings for "copy2".

Is it possible to replace the whole  "copy1" by "copy2" in a similar
way? It would be even better to directly select "copy1" or "copy2" based
on the current root filesystem, and not have to figure out the right
"-e" settings in the script or program that runs swupdate, but I'm not
sure this is possible as swupdate seems to want a "-e" option.

Anyway, following the same idea, I now have the check I initially
wanted, as implemented by this embedded script:

    embedded-script = "
function check_target_partition(image)
        current_root = swupdate.getroot().value
        swupdate.trace(\"Root =\" .. current_root)

        if current_root == image.device then
                swupdate.error(\"Refusing to install the update in the
current root partition. Update cancelled.\")
                return false, nil
        end
        return true, image
end
";

Thanks again.
Cheers

Stefano Babic

unread,
Aug 9, 2025, 2:07:19 PMAug 9
to Michael Opdenacker, swup...@googlegroups.com
Hi Michael,
You do not need "-e" at all to implement an A/B schema.

The selection "-e" remains very useful to switch between different modes
of operations. I have often such as template:

...

rescue : {
partitions: (...

images: (..

}

production: {

images: (..
files: (..
}


and the -e is used to switch between rescue, production, or whatever.

In case you have already a device in field using copy1, and copy2, it is
useful to use links :

my_current_setup : (
images...

}

copy1 : {

ref = "#./my_current_setup";
}
copy2 : {

ref = "#./my_current_setup";
}

>
> Anyway, following the same idea, I now have the check I initially
> wanted, as implemented by this embedded script:
>
>     embedded-script = "
> function check_target_partition(image)
>         current_root = swupdate.getroot().value
>         swupdate.trace(\"Root =\" .. current_root)
>
>         if current_root == image.device then
>                 swupdate.error(\"Refusing to install the update in the
> current root partition. Update cancelled.\")
>                 return false, nil
>         end
>         return true, image
> end
> ";
>

Best regards,
Stefano

Michael Opdenacker

unread,
Aug 12, 2025, 1:36:00 PMAug 12
to Stefano Babic, michael.o...@rootcommit.com, swup...@googlegroups.com
Hi Stefano

On 8/9/25 20:07, Stefano Babic wrote:
> You do not need "-e" at all to implement an A/B schema.
>
> The selection "-e" remains very useful to switch between different
> modes of operations. I have often such as template:
>
> ...
>
>     rescue : {
>         partitions: (...
>
>         images: (..
>
>     }
>
>     production: {
>
>         images: (..
>         files: (..
>     }
>
>
> and the -e is used to switch between rescue, production, or whatever.
>
> In case you have already a device in field using copy1, and copy2, it
> is useful to use links :
>
>     my_current_setup : (
>         images...
>
>     }
>
>     copy1 : {
>
>         ref = "#./my_current_setup";
>     }
>     copy2 : {
>
>         ref = "#./my_current_setup";
>     }
>

Thanks for the great tip! It allowed to factorize common parts in my
sw-description file:

        hardware-compatibility: [ "1.0"];

         stable : {
-            copy1 : {
-                images: ({
+           common_images : ({
                         filename =
"starlabs-image-starlabs-imx8mm.rootfs.ext4.gz";
                         type = "raw";
                         sha256 =
"$swupdate_get_sha256(starlabs-image-starlabs-imx8mm.rootfs.ext4.gz)";
                         compressed = "zlib";
                         device = "/dev/mmcblk0p1";
                        hook = "check_target_partition";
-                });
+           })
+
+           common_bootcmd : {
+                       name = "bootcmd";
+                       value = "sysboot mmc 0:${bootpart} any 49000000
/boot/extlinux/extlinux.conf";
+               }
+
+            copy1 : {
+                ref = "#./common_images";
                bootenv: (
                {
                        name = "bootpart";
                        value = "1";
                },
-               {
-                       name = "bootcmd";
-                       value = "sysboot mmc 0:${bootpart} any 49000000
/boot/extlinux/extlinux.conf";
-               }
+                ref = "#../common_bootcmd";
                )
             };
             copy2 : {
-                images: ({
-                        filename =
"starlabs-image-starlabs-imx8mm.rootfs.ext4.gz";
-                        type = "raw";
-                        sha256 =
"$swupdate_get_sha256(starlabs-image-starlabs-imx8mm.rootfs.ext4.gz)";
-                        compressed = "zlib";
-                        device = "/dev/mmcblk0p2";
-                       hook = "check_target_partition";
-                });
+                ref = "#./common_images";
                bootenv: (
                {
                        name = "bootpart";
                        value = "2";
                },
-               {
-                       name = "bootcmd";
-                       value = "sysboot mmc 0:${bootpart} any 49000000
/boot/extlinux/extlinux.conf";
-               }
+                ref = "#../common_bootcmd";
                )
             };
         };


This is much less error prone!

However, I still don't understand how I can implement A/B updates
without the "-e" option. I still need to let swupdate know on which
partition it should deploy the update, right?

Unless what you meant is to have one sw-description file for each target
partition. However, I prefer to have a shared one. This looks more
convenient on the build system (Yocto) side.
Thanks again

Stefano Babic

unread,
Aug 12, 2025, 2:11:14 PMAug 12
to Michael Opdenacker, Stefano Babic, swup...@googlegroups.com
Hi Michael,
You have already the solution. Adding the hooks, SWUpdate detects which
is the standby partition instead of relying on the value passed via
command line. With hook, you set the device (or file or whatever..)
where to install. And hooks are supported also for "bootenv", so you can
set the value via hook, too. The only "weird" thing is that a hook for
bootenv must set "version" for the value (image.version)

>
> Unless what you meant is to have one sw-description file for each target
> partition.

Never !

> However, I prefer to have a shared one. This looks more
> convenient on the build system (Yocto) side.

Of course - goal is often to have a single SWU, even for different purposes.

Best regards,
Stefano

> Thanks again
> Cheers
> Michael.
>

Storm, Christian

unread,
Aug 13, 2025, 5:44:58 AMAug 13
to swup...@googlegroups.com
Hi,


>> However, I still don't understand how I can implement A/B updates without the "-e" option. I still need to let swupdate know on which partition it should deploy the update, right?


Just as a note, there's also https://gitlab.com/cip-project/cip-sw-updates/swupdate-handler-roundrobin as a Lua handler that does A/B switching (and more stuff), in case you aren't aware.


Kind regards,
Christian

--
Dr. Christian Storm
Siemens AG, FT RPD CED
Friedrich-Ludwig-Bauer-Str. 3, 85748 Garching, Germany

ayoub...@googlemail.com

unread,
Aug 18, 2025, 3:00:05 AMAug 18
to swupdate
Hi,

another possibility is to use a swupdate-prepare.sh script that runs before swupdate and have something like:

#!/bin/sh
ROOT_DEV=$(swupdate -g)
MMC_DEV=${ROOT_DEV:0:12}
OFFSET=$((${#ROOT_DEV}-1))
CURRENT_PART=${ROOT_DEV:$OFFSET:1}
if [ $CURRENT_PART -eq "4" ]; then
UPDATE_PART="5";
else
UPDATE_PART="4";
fi
UPDATE_DEV="${MMC_DEV}p${UPDATE_PART}"
ln -sf ${UPDATE_DEV} /dev/update

so in you sw-description you set always:  device = "/dev/update";

best regards

Stefano Babic

unread,
Aug 19, 2025, 4:08:29 AMAug 19
to ayoub...@googlemail.com, swupdate
Hi Ayoub,

On 8/18/25 09:00, 'ayoub...@googlemail.com' via swupdate wrote:
> Hi,
>
> another possibility is to use a swupdate-prepare.sh script that runs
> before swupdate and have something like:
>
> #!/bin/sh
> ROOT_DEV=$(swupdate -g)
> MMC_DEV=${ROOT_DEV:0:12}
> OFFSET=$((${#ROOT_DEV}-1))
> CURRENT_PART=${ROOT_DEV:$OFFSET:1}
> if [ $CURRENT_PART -eq "4" ]; then
> UPDATE_PART="5";
> else
> UPDATE_PART="4";
> fi
> UPDATE_DEV="${MMC_DEV}p${UPDATE_PART}"
> ln -sf ${UPDATE_DEV} /dev/update
>
> so in you sw-description you set always:  device = "/dev/update";
>

This is what some vendors in their integration did, and there are some
drawbacks.

The first one is that this script must run outside the update process.
You cannot put the script inside the SWU, or you cannot use the
streaming feature.

This creates a (bad) dependency between the current running application
and the future (to be installed) release. In some way, the new software
depends on the previous. The Lua solution is much cleaner and it makes
the SWU self contained - it detects itself how the device runs and what
should be done.

A simpler use case where this breaks is in case you delivered devices
with different partition schemas, but you want of course that at the end
all devices will run exactly the same software in the same way. If the
SWU have no dependency from the running software, it can take different
decisions (even some repartitioning, whatever..).

Best regards,
Stefano



> best regards
> On Wednesday, August 13, 2025 at 11:44:58 AM UTC+2 Storm, Christian wrote:
>
> Hi,
>
>
> >> However, I still don't understand how I can implement A/B
> updates without the "-e" option. I still need to let swupdate know
> on which partition it should deploy the update, right?
>
>
> Just as a note, there's also https://gitlab.com/cip-project/cip-sw-
> updates/swupdate-handler-roundrobin <https://gitlab.com/cip-project/
> cip-sw-updates/swupdate-handler-roundrobin> as a Lua handler that
> does A/B switching (and more stuff), in case you aren't aware.
>
>
> Kind regards,
> Christian
>
> --
> Dr. Christian Storm
> Siemens AG, FT RPD CED
> Friedrich-Ludwig-Bauer-Str. 3, 85748 Garching, Germany
>
> --
> You received this message because you are subscribed to the Google
> Groups "swupdate" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to swupdate+u...@googlegroups.com
> <mailto:swupdate+u...@googlegroups.com>.
> To view this discussion visit https://groups.google.com/d/msgid/
> swupdate/1303a110-cbe7-4370-8512-96cc15e1924dn%40googlegroups.com
> <https://groups.google.com/d/msgid/swupdate/1303a110-
> cbe7-4370-8512-96cc15e1924dn%40googlegroups.com?
> utm_medium=email&utm_source=footer>.

ayoub...@googlemail.com

unread,
Aug 20, 2025, 2:46:08 AMAug 20
to swupdate

Hi Stefano,

From my side, I use both approaches. For the commercial products I’ve worked on, which use eMMC, the rootfs and redundant partitions are almost always encrypted using dm-crypt, so root: /dev/dm-0 and redundant: /dev/dm-1.

This setup is almost static, which makes it easy and avoids the need for any preparation scripts.

The LUA approach also has its downsides, since it relies on old swupdate(LUA Interface) running on the device to interpret newer scripts in SWU. This doesn’t always work and can even break updates, for example, when performing a long jump from an older to a much more recent SWUpdate version.


Best regards

Reply all
Reply to author
Forward
0 new messages