Closed mathaou closed 1 year ago
Hi @mathaou, we have support for Debian 10 - ARM64 (Refer to this doc: Device Update for IoT Hub supported platforms . The package can be found here: https://packages.microsoft.com/debian/10/prod/pool/main/d/deviceupdate-agent/ Debian 11 is not supported out of the box but the agent can be built for Deb 11 after updating dependencies.
Is there any guidance for this? Cross compilation steps? Regular build steps? Documentation or issues of this being done before?
A while back I was able to build adu and its dependencies on a Deb11 + RPi4 except I got stuck on building the DO local service--see #146. There are commits in the comments near the bottom of that issue. Also, as seen in that issue, Deb11 and raspian/rpios support will be added in an upcoming release.
Started work on cross-compilation to arm64 in this branch, but I am quite busy with feature work atm and won't be able to get back to it for some weeks. It is currently getting stuck where cmake is trying to include the cross-compiled/arm64 dependencies (such as azure iothub sdk and libdeliveryoptimization client SDK lib) that don't reside in the staging dir yet.
@jw-msft I see that the xcomp is currently trying to use toolchains and the like. It's a sad state of modern software, but the most reliable and painless way that I've found to xcomp is with a docker container + qemu.
Regarding the Deb11 support, is there any sort of timetable for when the next release will be available? By or before Q4 of this year perhaps?
Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42
There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow
The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well.
For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.
Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42
There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow
The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well.
For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.
Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.
IIRC from when I had tried a while back, one can build the core agent for Deb11 on device after commits https://github.com/Azure/iot-hub-device-update/commit/e283cf1a34dded4b7f59e66ad4b9b64679b7073d, https://github.com/Azure/iot-hub-device-update/commit/b80dac99b9200a9f43403851e631a30145abe993, and https://github.com/Azure/iot-hub-device-update/commit/150076b028f06d8374399ffd06033938fac39779 in develop branch and for azure-blob-storage-file-upload-utility repo commit https://github.com/Azure/azure-blob-storage-file-upload-utility/commit/ca52b773e3b62f1cfbeda551977fad80c98824d5. The next challenge is to build DO on Deb11 if using the DO content downloader (highly recommended for production device). If just evaluating for non-prod, then one could register the curl downloader - see https://github.com/Azure/iot-hub-device-update/blob/develop/packages/debian/postinst#L234 (use libcurl_content_downloader.so). The x-comp should hopefully land by oct timeframe.
Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42 There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well. For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.
Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.
@mathaou Ok, let me see if this week I can get you a dockerfile that will build it on ARM as a short-term workaround
Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42 There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well. For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.
Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.
@mathaou Ok, let me see if this week I can get you a dockerfile that will build it on ARM as a short-term workaround
I would really appreciate it thank you.
@mathaou This PR uses Hashicorp Packer to build a docker image that will contain the build artifacts for DU agent, its dependencies in /usr/local/lib/, and ability to run the unit tests.
It does not install DO (Delivery optimization) agent for downloading, so that would need to be done separately inside the running the container for now. Will see if I can get another iteration out Monday-ish to addres that.
@mathaou This PR uses Hashicorp Packer to build a docker image that will contain the build artifacts for DU agent, its dependencies in /usr/local/lib/, and ability to run the unit tests.
It does not install DO (Delivery optimization) agent for downloading, so that would need to be done separately inside the running the container for now. Will see if I can get another iteration out Monday-ish to addres that.
Thank you very much for your work! I will try this out Monday-ish, then.
I've added another commit to that PR that updates the README.md.
To build deliveryoptimization packages, use build/docker/debian11/arm64/Dockerfile
from this DO-client PR:
https://github.com/microsoft/do-client/pull/174
Do open that Dockerfile and read the comments in there to docker build the image (first, git clone https://github.com/microsoft/do-client --branch develop) and once in the container interactively by following the Dockerfile comments, cd to /code and follow the steps in /code/README.md to build agent and sdk, namely:
cd /code
python3 build/build.py --project agent --package-for deb
python3 build/build.py --project sdk --package-for deb
resultant .deb will be:
I've followed all of the steps and I have my *.deb files. Getting this error:
root@imx8mn-var-som:~# apt install ./libdeliveryoptimization_1.0.0_arm64.deb
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'libdeliveryoptimization' instead of './libdeliveryoptimization_1.0.0_arm64.deb'
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:
The following packages have unmet dependencies:
libdeliveryoptimization : Depends: deliveryoptimization-agent but it is not installable
E: Unable to correct problems, you have held broken packages.
Am I missing something?
I believe your issue may be that you have to install deliveryoptimization-agent .deb before libdeliveryoptimization .deb.
What is the device where the .deb packages are being installed and which debian 11 bullseye OS image is being used? Are there any clues in /var/log/dpkg.log?
It worked for me using:
hardware: RPI4
$ tail -n4 /proc/cpuinfo
Hardware : BCM2835
Revision : d03114
Serial : 10000000004cba37
Model : Raspberry Pi 4 Model B Rev 1.4
with Raspberry Pi OS Lite arm64 (based on Debian 11 Bullseye) using RPi Imager
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Here's what I did:
Raspberry Pi OS (other)
-> Raspberry Pi OS Lite (64-bit)
and wrote it to a 128 GB SD card-rw-r--r-- 1 jw jw 2018782 Aug 15 18:02 libdeliveryoptimization_1.0.0_arm64.deb
-rw-r--r-- 1 jw jw 3572436 Aug 15 18:02 deliveryoptimization-agent_1.0.0_arm64.deb
-rw-r--r-- 1 jw jw 6169026 Aug 15 18:05 deviceupdate-agent_1.0.2_arm64.deb
Then I installed the .deb packages with the following:
sudo apt install .\deliveryoptimization-agent_1.0.0_arm64.deb
sudo apt install .\libdeliveryoptimization_1.0.0_arm64.deb
sudo apt install .\deviceupdate-agent_1.0.2_arm64.deb
Attached here are the /var/log/dpkg.log
and /var/log/apt/term.log
that show the above working.
rpi4_var_log_apt_term.log
rpi4_var_log_dpkg.log
Then I copied over a known-working du-config.json to /etc/adu/ and it ran fine, including downloading the update payload using DO-agent, installing the APT package using APT Handler, and reporting back 700 (Apply succeeded) in the twin: adu-shell.20230815-190556.log apt-handler.20230815-190556.log du-agent.20230815-190544.log
THANK YOU
It works fine!
Glad it worked for you, @matthewfarstad, and thanks for verifying it.
Reopening because I am now getting into a "Failed parse of JSON file: /etc/adu/du-config.json" loop for deviceupdate-agent.
Aug 18 20:18:05 imx8mn-var-som systemd[1]: Started Device Update Agent daemon.. Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2661Z 91683[91683] [E] Failed parse of JSON file: /etc/adu/du-config.json [ADUC_ConfigInfo_Init:360] Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2662Z 91683[91683] [E] Cannot initialize config info. [main:925] Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: WARNING: Unable to start file logger. (Log folder: /var/log/adu) Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2662Z 91683[91683] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525] Aug 18 20:18:05 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Main process exited, code=exited, status=255/EXCEPTION Aug 18 20:18:05 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Failed with result 'exit-code'.
-rw-r--r-- 1 adu adu 507 Aug 18 20:04 /etc/adu/du-config.json
cat <<EOF | tee -a /etc/adu/du-config.json
{
"schemaVersion": "1.0",
"aduShellTrustedUsers": [
"adu",
"do"
],
"manufacturer": "kappa",
"model": "vacuum",
"agents": [
{
"name": "main",
"runas": "adu",
"connectionSource": {
"connectionType": "string",
"connectionData": "HostName=${__HOSTNAME};DeviceId=${__MAC};SharedAccessKey=${__SHARED_ACCESS_KEY}"
},
"manufacturer": "kappa",
"model": "vacuum"
}
]
}
EOF
chown adu:adu /etc/adu/du-config.json
chown root:adu /usr/bin/adu-shell
chmod u=rxs /usr/bin/adu-shell
Documentation says:
Daemon When the Device Update Agent has been installed as a daemon, it will automatically start on boot. The Device Update Agent daemon reads the IoT Hub device connection string from a configuration file. Place your device connection string in /adu/adu-conf.txt.
So how do I get this to work? Some info says to use the .json file, older info says to use the conf.txt... I swear I just had it working but I was doing a million other things and of course didn't write down steps and just assumed it would stay that way...
I gave all files the permissions they needed and now I get this:
Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7089Z 117984[117984] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7097Z 117984[117984] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7097Z 117984[117984] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] Agent exited with code 1 [main:1101] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [W] Agent is shutting down. [ShutdownAgent:812] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7099Z 117984[117984] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525] Aug 18 20:24:10 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Main process exited, code=exited, status=1/FAILURE Aug 18 20:24:10 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Failed with result 'exit-code'.
@jw-msft don't know how to reopen so pinging sorry
root@imx8mn-var-som:~# AducIotAgent 'device connection string' 2023-08-18T20:26:18.9768Z 127218[127218] [I] Agent (linux; 1.0.2) starting. [main:1021] 2023-08-18T20:26:18.9769Z 127218[127218] [I] Git Info: develop:e5e41f8a [main:1023] 2023-08-18T20:26:18.9769Z 127218[127218] [I] Supported Update Manifest version: min: 4, max: 5 [main:1025] 2023-08-18T20:26:18.9778Z 127218[127218] [I] Health check passed. [HealthCheck:632] 2023-08-18T20:26:18.9789Z 127218[127218] [I] Initializing PnP components. [ADUC_PnP_Components_Create:493] 2023-08-18T20:26:18.9789Z 127218[127218] [I] ADUC agent started. Using IoT Hub Client SDK 1.7.0 [AzureDeviceUpdateCoreInterface_Create:257] 2023-08-18T20:26:18.9790Z 127218[127218] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] 2023-08-18T20:26:18.9791Z 127218[127218] [E] Unable to initialize the diagnostic workflow data. [DiagnosticsInterface_Create:68] 2023-08-18T20:26:18.9793Z 127218[127218] [E] Failed to initialize PnP component 'diagnosticInformation'. [ADUC_PnP_Components_Create:503] 2023-08-18T20:26:18.9794Z 127218[127218] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:26:18.9794Z 127218[127218] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] 2023-08-18T20:26:18.9794Z 127218[127218] [E] ADUC_PnP_Components_Create failed [StartupAgent:760] 2023-08-18T20:26:18.9794Z 127218[127218] [I] Agent exited with code 1 [main:1101] 2023-08-18T20:26:18.9795Z 127218[127218] [W] Agent is shutting down. [ShutdownAgent:812] 2023-08-18T20:26:18.9795Z 127218[127218] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] 2023-08-18T20:26:18.9795Z 127218[127218] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:26:18.9796Z 127218[127218] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]
This is what the deb pkg runs, so you could run it manually like this:
wget https://raw.githubusercontent.com/Azure/iot-hub-device-update/develop/packages/debian/postinst
chmod 755 ./postinst
./postinst configure
Same error after running that:
2023-08-18T20:36:39.9617Z 172036[172036] [I] Agent (linux; 1.0.2) starting. [main:1021] 2023-08-18T20:36:39.9618Z 172036[172036] [I] Git Info: develop:e5e41f8a [main:1023] 2023-08-18T20:36:39.9618Z 172036[172036] [I] Supported Update Manifest version: min: 4, max: 5 [main:1025] 2023-08-18T20:36:39.9626Z 172036[172036] [I] Health check passed. [HealthCheck:632] 2023-08-18T20:36:39.9641Z 172036[172036] [I] Initializing PnP components. [ADUC_PnP_Components_Create:493] 2023-08-18T20:36:39.9643Z 172036[172036] [I] ADUC agent started. Using IoT Hub Client SDK 1.7.0 [AzureDeviceUpdateCoreInterface_Create:257] 2023-08-18T20:36:39.9643Z 172036[172036] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] 2023-08-18T20:36:39.9644Z 172036[172036] [E] Unable to initialize the diagnostic workflow data. [DiagnosticsInterface_Create:68] 2023-08-18T20:36:39.9646Z 172036[172036] [E] Failed to initialize PnP component 'diagnosticInformation'. [ADUC_PnP_Components_Create:503] 2023-08-18T20:36:39.9647Z 172036[172036] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:36:39.9647Z 172036[172036] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] 2023-08-18T20:36:39.9647Z 172036[172036] [E] ADUC_PnP_Components_Create failed [StartupAgent:760] 2023-08-18T20:36:39.9647Z 172036[172036] [I] Agent exited with code 1 [main:1101] 2023-08-18T20:36:39.9648Z 172036[172036] [W] Agent is shutting down. [ShutdownAgent:812] 2023-08-18T20:36:39.9648Z 172036[172036] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] 2023-08-18T20:36:39.9648Z 172036[172036] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:36:39.9649Z 172036[172036] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]
Is /etc/adu/du-diagnostics-config.json
existing and have adu:adu
ownership?
i.e.
chown adu:adu /etc/adu/du-diagnostics-config.json
chmod 440 /etc/adu/du-diagnostics-config.json
It should have contents similar to the following, where each logComponent array element is a dir of files you want to ship to blob storage (when diagnostics retrieval is invoked in the azure portal):
{
"logComponents":[
{
"componentName":"adu",
"logPath":"/var/log/adu/"
},
{
"componentName":"do",
"logPath":"/var/log/deliveryoptimization-agent/"
}
],
"maxKilobytesToUploadPerLogPath":256
}
The [E] Unable to initialize the diagnostic workflow data.
trace being seen corresponds to this line in DiagnosticsInterface_Create
that calls DiagnosticsConfigUtils_InitFromFile
.
DiagnosticsConfigUtils_InitFromFile fn def'n is here
I have my daemon running properly, but I thought my device status was supposed to change to connected or something...
Or I have this in my device update section, now. I think that is correct...
f8dc7a8b90f2 NA NA NA Yes
Alright! I think I have everything in place knock on wood.
The final question I have is: what constitutes an update being available? My .importmanifest.json, .swu file, and /etc/adu-version all have this info available, but I can't seem to get an update available for my ADUGroup tag I'm targeting (there are no new updates for this group) after I import an update. Just thought I would ask if there was some hidden step.
I got an update to go through once on a fresh device, but it failed because I didn't have /etc/adu-version. I put 1.0.0 in there, so I hope that was correct.
Got it sorted. model was different.
I have my daemon running properly, but I thought my device status was supposed to change to connected or something...
- If agent has an mqtt or mqtt/ws connection to the hub successfully it should show "Connected" value in the twin for the top-level "connectionState" property.
- From the device running the daemon for the deviceupdate-agent.service, (if
sudo systemctl status deviceupdate-agent
shows it's running), doing:netstat -pant | grep ':8883'
should show anESTABLISHED
tcp socket from device ip:port to the hub ip:8883 (secure mqtt) or ip:443 (mqtt over websocket), depending on du-config.json iotHubProtocol setting.The final question I have is: what constitutes an update being available? My .importmanifest.json, .swu file, and /etc/adu-version all have this info available, but I can't seem to get an update available for my ADUGroup tag I'm targeting (there are no new updates for this group) after I import an update. Just thought I would ask if there was some hidden step.
Check for alignment of compatibility property in the import manifest and the following properties in du-config.json:
After publish of update, then the service will offer a link to Deploy the latest version of update for the ADUGroup device group (or for the Default group if none specified in twin) in portal ux under DeviceManagement -> Updates -> "Groups and Deployments" tab for the matching device group based on (by default) the model and manufacturer in du-config.json and update's compatibility properties.
I got an update to go through once on a fresh device, but it failed because I didn't have /etc/adu-version. I put 1.0.0 in there, so I hope that was correct.
Yes, the isInstalled check of the swupdate handlers uses the contents of /etc/adu-version will be compared with value of installedCriteria property under handlerProperties in import manifest.
Is there any way to have a range of that? Like "this update applies to anything 1.0.0 and higher"?
Or are the updates in azure kind of just automatically sandwiched together, i.e. it will find the update that moves 1.0.0 to 1.0.1, then find the one that moves 1.0.1 to 1.0.3 etc
Is there any way to have a range of that? Like "this update applies to anything 1.0.0 and higher"?
That is currently not provided by default by 1st party step handlers (which are based on matching installedCriteria string). One would have to encode it in the installedCriteria string or via custom handlerProperties and write a custom handler that interprets the handler properties to achieve that. e.g. one could add "minVersion" and "maxVersion" (either could have a +/- infinity) to the handlerProperties and implement the logic in the custom handler.
@eshashah-msft to provide link for feature voting or to track this feature request. Starting a github discussion would also be a good venue for discussing this further.
Or are the updates in azure kind of just automatically sandwiched together, i.e. it will find the update that moves 1.0.0 to 1.0.1, then find the one that moves 1.0.1 to 1.0.3 etc
This latter one. For the provider and name, one would add updates with incremented version. See https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-deployments for more info.
@josephmsft Could you elaborate further?
So I got a successful update, but /etc/adu-version didn't update to the correct version numbers. I'll run another update in morning and clear the logs of all my bad runs to verify whats going on.
Also I'm now trapped in an infinite update loop even though the IoT hub says this device is on latest update (reported update was successful, and then it switched to update in progress even though on latest). Was /etc/adu-version supposed to update? Because its the same.
Installing update.
0[TRACE] : SWUPDATE running : [load_cert_chain] : failed: BIO_new_file(/adukey/public.pem)
0[ERROR] : SWUPDATE failed [0] ERROR : Error loading certificate chain from /adukey/public.pem
Error: Crypto cannot be initialized.
Applying update.
Do preinstall and postinstall scripts for swupdate handler not work? Or basically how do I get out of this loop, I want a post install script that triggers booting into another partition.
UPDATE: I see it is recommended to use v2 swupdate handler. The README.md says I need a handler file? Where is that for microsoft/swupdate:2?
Banking on it just being included in what I have already. Basing a script off of this example: https://github.com/Azure/iot-hub-device-update/tree/main/src/extensions/step_handlers/swupdate_handler_v2/tests/testdata/adu-yocto-ab-rootfs-update
2023-08-22T15:05:06.2716Z 453020[453159] [I] Loading handler for step #0 (handler: 'microsoft/swupdate:2') [StepsHandler_Download:606]
2023-08-22T15:05:06.2717Z 453020[453159] [I] Loading handler for 'microsoft/swupdate:2'. [LoadUpdateContentHandlerExtension:208]
2023-08-22T15:05:06.2719Z 453020[453159] [I] Loading extension 'microsoft/swupdate:2'. Reg file : /var/lib/adu/extensions/update_content_handlers/microsoft_swupdate_2/content_handler.json [LoadExtensionLibrary:85]
2023-08-22T15:05:06.2723Z 453020[453159] [I] Instantiating a Step Handler for 'microsoft/swupdate:2' [CreateUpdateContentHandlerExtension:30]
2023-08-22T15:05:06.2724Z 453020[453159] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]
2023-08-22T15:05:06.2725Z 453020[453159] [I] SWUpdate handler v2 download task begin. [Download:326]
2023-08-22T15:05:06.2725Z 453020[453020] [I] Property osName changed to Debian GNU/Linux [RefreshDeviceInfoInterfaceData:131]
2023-08-22T15:05:06.2726Z 453020[453159] [I] SWUpdate_Handler download task end. [Download:389]
2023-08-22T15:05:06.2727Z 453020[453159] [I] Action 'Download' complete. Result: 0 (failed), 806355203 (0x30100103) [ADUC_Workflow_WorkCompletionCallback:896]
2023-08-22T15:05:06.2727Z 453020[453159] [I] WorkCompletionCallback: Download failed. Going to state Failed [ADUC_Workflow_WorkCompletionCallback:1021]
2023-08-22T15:05:06.2727Z 453020[453159] [I] Setting UpdateState to Failed [ADUC_Workflow_SetUpdateStateHelper:1093]
I'm trying this approach:
#!/bin/bash
# create importmanifest
__VERSION="1.0.1"
az iot du update init v5 \
--update-provider test \
--update-name one \
--update-version $__VERSION \
--compat manufacturer=test model=v1 \
--step handler=microsoft/swupdate:2 properties="{\"installedCriteria\": \"$__VERSION\", \"scriptFileName\": \"update.sh\"}" \
--file path=$1 > test_${__VERSION}.importmanifest.json
With this swdescription file
software =
{
version = "1.0.1";
stable = {
A: {
files: (
{
filename = "test";
path = "/mnt/usr/bin/test";
device = "/dev/mmcblk2p2";
filesystem = "/mnt";
properties = {create-destination = "true"; atomic-install = "true";}
}
);
}
B: {
files: (
{
filename = "test";
path = "/mnt/usr/bin/test";
device = "/dev/mmcblk2p1";
filesystem = "/mnt";
properties = {create-destination = "true"; atomic-install = "true";}
}
);
}
};
}
With this update file. I tried following several things from the example. I know that there have to be the 5 supported cli ops (--action-...), but it seemed that there were a number of things being passed implicitly to the script. I included this script in the .swu archive (not sure if it has to exist on device)
Its unclear when this gets called and by what - I added the --swu-file and --installed-criteria cli params but I'm not sure that was correct
(https://github.com/Azure/iot-hub-device-update/blob/main/src/extensions/step_handlers/swupdate_handler_v2/README.md) tells me that the --installed-criteria gets passed.
#!/bin/bash
software_version_file="/etc/adu-version"
result_file=/adu/logs/swupdate.result.json
swupdate_log_file=/adu/logs/swupdate_log_file
current_partition=1 # assume we're in partition 1
update_partition=2
selection="stable,B"
image_file="" # for install task
rootfs_dev=$(mount | grep "on / type" | cut -d':' -f 2 | cut -d' ' -f 1)
if ! [[ $rootfs_dev == '/dev/mmcblk2p1' ]]; then # switch to assuming we're in partition 2
current_partition=2
update_partition=1
selection="stable,A"
fi
# --action-download : No-op. (No additional payloads)
# --action-install : Invokes swupdate to install the specified 'swuFile'
# --action-apply : Sets uboot environment variable to switch to the desired rootfs partition after rebooted.
# Returns a special (ADUC_Result) result code to instruct DU Agent to reboot the device.
# --action-cancel : Restore uboot environment variable to the value before '--action-apply' is performed.
# --action-is-installed : Returns (ADUC_Result) result code that indicates whether the current rootfs contains
# software (image) version that match specified 'installedCriteria' string.
__ret_val=0
if [[ $0 != "${BASH_SOURCE[0]}" ]]; then
ret='return'
else
ret='exit'
fi
make_swupdate_handler_erc() {
local base_erc=0x30101000
local -n res=$2 # name reference
res=$((base_erc + $1))
}
make_aduc_result_json() {
local -n res=$4 # name reference
res="{\"resultCode\":$1, \"extendedResultCode\":$2,\"resultDetails\":\"$3\"}"
}
print_usage() {
echo "heyo"
}
# Usage: is_installed $installedCriteria $imageVersionFile <out resultCode> <out extendedResultCode> <out resultDetails>
is_installed() {
local -n rc=$3 # name reference for resultCode
local -n erc=$4 # name reference for extendedResultCode
local -n rd=$5 # name reference for resultDetails
__current_version=$(cat $software_version_file)
if ! [[ -f $2 ]]; then
rc=0
make_swupdate_handler_erc 100 erc
rd="Image version file name is empty."
elif [[ $1 == "" ]]; then
rc=0
make_swupdate_handler_erc 101 erc
rd="Installed criteria is empty."
else
if [ $__current_version == "$1" ]; then
rc=900
else
rc=901
fi
erc=0
rd=""
fi
}
result() {
echo "$@" > "$result_file"
}
check_is_installed_state() {
local local_rc=2
local local_erc=3
local local_rd="4"
local aduc_result=""
# Evaluates installedCriteria string.
is_installed "$1" "$software_version_file" local_rc local_erc local_rd
make_aduc_result_json "$local_rc" "$local_erc" "$local_rd" aduc_result
# Show output.
echo "Result: $aduc_result"
# Write ADUC_Result to result file.
result "$aduc_result"
}
download_update_artifacts() {
# Return ADUC_Result_Download_Success (500), no extended result code, no result details.
local aduc_result=""
make_aduc_result_json 500 0 "" aduc_result
# Show output.
echo "Result: $aduc_result"
# Write ADUC_Result to result file.
result "$aduc_result"
$ret $__ret_val
}
install_update() {
resultCode=0
extendedResultCode=0
resultDetails=""
ret_val=1
is_installed "$installed_criteria" "$software_version_file" resultCode extendedResultCode resultDetails
is_installed_ret=$?
if [[ $is_installed_ret -ne 0 ]]; then
# is_installed functin failed to execute.
resultCode=0
make_swupdate_handler_erc "$is_installed_ret" extendedResultCode
resultDetails="Internal error in 'is_installed' function."
elif [[ $resultCode == 0 ]]; then
# Failed to determine whether the update has been installed or not.
# Return current ADUC_Result
echo "Failed to determine wehther the update has been installed or not."
elif [[ $resultCode -eq 901 ]]; then
# Not installed.
# install an update.
echo "Installing update." >> "${log_file}"
if [[ -f $image_file ]]; then
# Note: Swupdate can use a public key to validate the signature of an image.
#
# Here is how we generated the private key for signing the image
# and how we generated that public key file used to validate the image signature.
#
# Generated RSA private key with password using command:
# openssl genrsa -aes256 -passout file:priv.pass -out priv.pem
#
# Generated RSA public key from private key using command:
# openssl rsa -in ${WORKDIR}/priv.pem -out ${WORKDIR}/public.pem -outform PEM -pubout
ret_val=1
# get this working at some point @todo
# if [[ ${public_key_file} -eq "" ]]; then
# # Call swupdate with the image file and no signature validations
# swupdate -v -i "${image_file}" -e ${selection} &>> "${swupdate_log_file}"
# ret_val=$?
# else
# # Call swupdate with the image file and the public key for signature validation
# swupdate -v -i "${image_file}" -k "${public_key_file}" -e ${selection} &>> "${swupdate_log_file}"
# ret_val=$?
# fi
swupdate -v -i "${image_file}" -e ${selection} &>> "${swupdate_log_file}"
ret_val=$?
if [[ $ret_val -eq 0 ]]; then
resultCode=600
extendedResultCode=0
resultDetails=""
else
resultCode=0
make_swupdate_handler_erc "$ret_val" extendedResultCode
resultDetails="SWUpdate command failed."
fi
else
echo "Image file ${image_file} was not found." >> "${log_file}"
resultCode=0
# ADUC_ERC_SWUPDATE_HANDLER_INSTALL_FAILURE_IMAGE_FILE_NOT_FOUND (0x202)
extendedResultCode=0x30100202
resultDetails="Image file ${image_file} was not found."
fi
ret_val=0
else
# Installed.
log_info "It appears that this component already installed the specified update."
resultCode=603
extendedResultCode=0
resultDetails="Already installed."
ret_val=0
fi
make_aduc_result_json "$resultCode" "$extendedResultCode" "$resultDetails" aduc_result
# Show output.
echo "Result: $aduc_result"
# Write ADUC_Result to result file.
result "$aduc_result"
$ret $ret_val
}
apply_update() {
ret_val=0
resultCode=0
extendedResultCode=0
resultDetails=""
fw_setenv mmcpart $update_partition
ret_val=$?
if [[ $ret_val -eq 0 ]]; then
resultCode=705 # restart immediately
extendedResultCode=0
resultDetails=""
else
resultCode=0
make_swupdate_handler_erc "$ret_val" extendedResultCode
resultDetails="Cannot set rpipart value to $update_partition"
echo "$resultDetails"
fi
}
cancel_update() {
echo "CancelUpdate called"
resultCode=0
extendedResultCode=0
resultDetails=""
ret_val=
fw_setenv mmcpart $current_partition
ret_val=$?
if [[ $ret_val -eq 0 ]]; then
resultCode=800
else
resultCode=801
make_swupdate_handler_erc "$ret_val" extendedResultCode
resultDetails="Failed to set rpipart to ${update_partition}"
ret_val=0
fi
make_aduc_result_json "$resultCode" "$extendedResultCode" "$resultDetails" aduc_result
echo "Result: $aduc_result"
# Write ADUC_Result to result file.
result "$aduc_result"
$ret $ret_val
}
for arg in "$@"; do
shift
case "$arg" in
'--action-download') set -- "$@" '-d' ;;
'--action-install') set -- "$@" '-i' ;;
'--action-apply') set -- "$@" '-a' ;;
'--action-cancel') set -- "$@" '-c' ;;
'--action-is-installed') set -- "$@" '-x' ;;
'--swu-file') set -- "$@" '-f' ;;
'--installed-criteria') set -- "$@" '-r' ;;
*) set -- "$@" "$arg" ;;
esac
done
installed_criteria=
check_is_installed=
while getopts "f:r:xcaid" opt
do
case "$opt" in
'd') download_update_artifacts; exit $__ret_val ;;
'i') install_update; exit $__ret_val ;;
'a') apply_update; exit $__ret_val ;;
'c') cancel_update; exit $__ret_val ;;
'x') check_is_installed=yes ;;
'f') image_file=$OPTARG ;;
'r') installed_criteria=$OPTARG ;;
'?') print_usage >&2; exit 1 ;;
esac
done
if [ -n "$check_is_installed" ]; then
check_is_installed_state $installed_criteria
exit $__ret_val
fi
$ret $__ret_val
I believe my script should be on the device since when I use the swupdate:1 handler I see function names that appear in that example. When I try to register swupdate:2 handler with this command I get bad output:
root@imx8mn-var-som:~# /usr/bin/AducIotAgent --update-type 'microsoft/swupdate:2' --register-content-handler /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so
Unsupported option '--update-type'. Try preceding with -- to separate options and additional args.
Attempting to just use the microsoft/swupdate:1 handler. I put my custom script in /usr/lib/adu/adu-swupdate.sh an update success - but my /etc/adu-version didn't update and my update logic didn't get triggered. Going to assume that the script I based mine off of was for the microsoft/swupdate:2 handler instead of microsoft/swupdate:1.
Basing a new script off the one that came with install:
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# Ensure that getopt starts from first option if ". <script.sh>" was used.
OPTIND=1
# Ensure we dont end the user's terminal session if invoked from source (".").
if [[ $0 != "${BASH_SOURCE[0]}" ]]; then
ret='return'
else
ret='exit'
fi
print_help() {
echo "adu-swupdate.sh [-a] [-h] [-i image_file] [-l log_dir] [-r] "
echo "-a Applies the install by telling the bootloader to boot to the updated partition."
echo "-h Show this help message."
echo "-i image_file The update image file to install. For swupdate this is typically a .swu file"
echo "-l log_dir The folder where logs should be written."
echo "-r Reverts the apply by telling the bootloader to boot into the current partition."
}
action=""
num_actions=0
log_dir="/tmp"
while getopts "ahi:l:r" opt; do
case "$opt" in
a)
action="apply"
num_actions=$((num_actions + 1))
;;
h)
print_help
$ret 0
;;
i)
action="install"
image_file=$OPTARG
num_actions=$((num_actions + 1))
;;
l)
log_dir=$OPTARG
;;
r)
action="revert"
num_actions=$((num_actions + 1))
;;
\?)
print_help
$ret 1
;;
esac
done
# Check that an option was specified
if [[ $num_actions == 0 ]]; then
echo "Must specify one action command line option."
print_help
$ret 1
fi
# Check that only one option was specified
if [[ $num_actions -gt 1 ]]; then
echo "Must specify only one action command line option."
print_help
$ret 1
fi
# Make sure the log directory is created.
mkdir -p "$log_dir"
# SWUpdate doesn't support everything necessary for the dual-copy or A/B update strategy.
# Here we figure out the current OS partition and then set some environment variables
# that we use to tell swupdate which partition to target.
rootfs_dev=$(mount | grep "on / type" | cut -d':' -f 2 | cut -d' ' -f 1)
if [[ $rootfs_dev == '/dev/mmcblk0p2' ]]; then
selection="stable,copy2"
current_part=2
update_part=3
else
selection="stable,copy1"
current_part=3
update_part=2
fi
if [[ $action == "apply" ]]; then
# Set the bootloader environment variable
# to tell the bootloader to boot into the current partition
# instead of the one that was updated.
# rpipart variable is specific to our boot.scr script.
echo "Applying update." >> "${log_dir}/swupdate.log"
fw_setenv rpipart $update_part
$ret $?
fi
if [[ $action == "revert" ]]; then
# Set the bootloader environment variable
# to tell the bootloader to boot into the current partition
# instead of the one that was updated.
# rpipart variable is specific to our boot.scr script.
echo "Reverting update." >> "${log_dir}/swupdate.log"
fw_setenv rpipart $current_part
$ret $?
fi
if [[ $action == "install" ]]; then
echo "Installing update." >> "${log_dir}/swupdate.log"
if [[ -f $image_file ]]; then
# Swupdate will use a public key to validate the signature of an image.
# Here is how we generated the private key for signing the image
# and how we generated that public key file used to validate the image signature.
# Generated RSA private key with password using command:
# openssl genrsa -aes256 -passout file:priv.pass -out priv.pem
# Generated RSA public key from private key using command:
# openssl rsa -in ${WORKDIR}/priv.pem -out ${WORKDIR}/public.pem -outform PEM -pubout
# Call swupdate with the image file and the public key for signature validation
swupdate -v -i "${image_file}" -k /adukey/public.pem -e ${selection} &>> "${log_dir}/swupdate.log"
$ret $?
else
echo "Image file $image_file was not found." >> "${log_dir}/swupdate.log"
$ret 1
fi
fi
Also I'm now trapped in an infinite update loop even though the IoT hub says this device is on latest update (reported update was successful, and then it switched to update in progress even though on latest). Was /etc/adu-version supposed to update? Because its the same. Do preinstall and postinstall scripts for swupdate handler not work? Or basically how do I get out of this loop, I want a post install script that triggers booting into another partition.
I'd suggest temporarily stopping deviceupdate-agent service and manually running the swupdate cmd-line with strace to understand why Error: Crypto cannot be initialized
. swupdate needs to use the public.pem to verify the sw-description signature, but since it's failing I would've expected exit code to be non-zero from swupdate and it should have led to an install failure as seen in the code here
Start by running swupdate manually with -v
verbose flag and if need more info then run /usr/bin/swupdate
again with strace
(and maybe again with ltrace
after that) on the front so we can see the syscalls and hopefully determine what is failing when trying to init crypto:
systemctl stop deviceupdate-agent
ps -efww | grep [A]duc
/usr/bin/swupdate -v -i ./adu-update-image-raspberrypi3-1.0.2-2.swu \
-k /adukey/public.pem -e 'stable,B' | tee /tmp/swupdate.strace.log 2>&1
echo $?
Here's my result when running on RPi3 flashed with the wic and where I transferred over the .swu:
# sd-card flashed with adu-base-image-raspberrypi3-1.0.2.wic.gz inside
# of https://github.com/Azure/iot-hub-device-update/releases/download/1.0.2/Tutorial_RaspberryPi.zip
# scp ./adu-update-image-raspberrypi3-1.0.2-2.swu to rpi first
$ /usr/bin/swupdate -v \
-i ./adu-update-image-raspberrypi3-1.0.2-2.swu \
-k /adukey/public.pem -e 'stable,copy2'
SWUpdate v
Licensed under GPLv2. See source distribution for detailed copyright notices.
[INFO ] : SWUPDATE running : [print_registered_bootloaders] : Registered bootloaders:
[INFO ] : SWUPDATE running : [print_registered_bootloaders] : uboot loaded.
[INFO ] : SWUPDATE running : [print_registered_bootloaders] : none loaded.
[INFO ] : SWUPDATE running : [main] : Using default bootloader interface: uboot
[INFO ] : SWUPDATE running : [main] : Running on raspberrypi3 Revision 1.0
[INFO ] : SWUPDATE running : [print_registered_handlers] : Registered handlers:
[INFO ] : SWUPDATE running : [print_registered_handlers] : dummy
[INFO ] : SWUPDATE running : [print_registered_handlers] : archive
[INFO ] : SWUPDATE running : [print_registered_handlers] : tar
[INFO ] : SWUPDATE running : [print_registered_handlers] : uboot
[INFO ] : SWUPDATE running : [print_registered_handlers] : bootloader
[INFO ] : SWUPDATE running : [print_registered_handlers] : lua
[INFO ] : SWUPDATE running : [print_registered_handlers] : raw
[INFO ] : SWUPDATE running : [print_registered_handlers] : rawfile
[INFO ] : SWUPDATE running : [print_registered_handlers] : rawcopy
[INFO ] : SWUPDATE running : [print_registered_handlers] : shellscript
[INFO ] : SWUPDATE running : [print_registered_handlers] : preinstall
[INFO ] : SWUPDATE running : [print_registered_handlers] : postinstall
[INFO ] : SWUPDATE running : [main] : software set: stable mode: copy2
[TRACE] : SWUPDATE running : [listener_create] : creating socket at /tmp/swupdateprog
[TRACE] : SWUPDATE running : [network_initializer] : Main loop daemon
[TRACE] : SWUPDATE running : [listener_create] : creating socket at /tmp/sockinstctrl
[TRACE] : SWUPDATE running : [network_thread] : Incoming network request: processing...
[INFO ] : SWUPDATE started : Software Update started !
[TRACE] : SWUPDATE running : [network_initializer] : Software update started
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : Found file
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : filename sw-description
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : size 1007
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : Found file
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : filename sw-description.sig
[TRACE] : SWUPDATE running : [extract_file_to_tmp] : size 256
[TRACE] : SWUPDATE running : [swupdate_verify_file] : Verify signed image: Read 1007 bytes
[TRACE] : SWUPDATE running : [swupdate_verify_file] : Verified OK
[TRACE] : SWUPDATE running : [get_common_fields] : Version 1.0.2-2
[TRACE] : SWUPDATE running : [parse_hw_compatibility] : Accepted Hw Revision : 1.0
[TRACE] : SWUPDATE running : [_parse_images] : Found compressed Image: adu-base-image-raspberrypi3.ext4.gz in device : /dev/mmcblk0p3 for handler raw
[TRACE] : SWUPDATE running : [check_hw_compatibility] : Hardware raspberrypi3 Revision: 1.0
[TRACE] : SWUPDATE running : [check_hw_compatibility] : Hardware compatibility verified
[TRACE] : SWUPDATE running : [extract_files] : Found file
[TRACE] : SWUPDATE running : [extract_files] : filename adu-base-image-raspberrypi3.ext4.gz
[TRACE] : SWUPDATE running : [extract_files] : size 124609949 required
[TRACE] : SWUPDATE running : [extract_padding] : Expecting 484 padding bytes at end-of-file
[TRACE] : SWUPDATE running : [network_initializer] : Valid image found: copying to FLASH
[INFO ] : SWUPDATE running : Installation in progress
[TRACE] : SWUPDATE running : [install_single_image] : Found installer for stream adu-base-image-raspberrypi3.ext4.gz raw
[INFO ] : SWUPDATE successful ! SWUPDATE successful !
[TRACE] : SWUPDATE running : [network_initializer] : Main thread sleep again !
[INFO ] : No SWUPDATE running : Waiting for requests...
[INFO ] : SWUPDATE running : [endupdate] : SWUpdate was successful !
$ echo $?
0
I believe my script should be on the device since when I use the swupdate:1 handler I see function names that appear in that example. When I try to register swupdate:2 handler with this command I get bad output:
The cmd to register used that failed was using older cmdline switches:
/usr/bin/AducIotAgent --update-type 'microsoft/swupdate:2' \
--register-content-handler /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so
Try this instead as per the newest deb pkg postinst script:
/usr/bin/AducIotAgent --extension-type updateContentHandler \
--extension-id 'microsoft/swupdate:2' \
--register-extension /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so
However, it should already be registered as per its content_handler.json so running the registration likely won't change it:
$ cat /var/lib/adu/extensions/update_content_handlers/microsoft_swupdate_2/content_handler.json
{
"fileName": "/var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so",
"sizeInBytes": 198384,
"hashes": {
"sha256": "fjKbCUxqr75Ue1Bai34qQ4eOQqt/JhfcCqB81hYVdgg="
},
"handlerId": "microsoft/swupdate:2"
}
Why would there be no reported update available in azure?
Could *.importManifest.json that was uploaded also be provided? Also "deviceId" from the device twin would help for looking on the backend.
Did the netstat from https://github.com/Azure/iot-hub-device-update/issues/484#issuecomment-1686875288 show agent was connected to port 8883 and did "connectionStatus" in the twin change to "Connected"?
I think I got everything working. However, DU is saying that I am at 8/10 device classes for my tier. I see the next tier up only goes to 80, what is a device class and how I do I scale that to have hundreds of devices? I want to update remotely potentially thousands of devices, so is this the point I have to call someone at MSFT or what?
A device class can be thought of as a set of devices that are all compatible with the same updates. In practice this means they all have the same PnP modelId and compat properties. You can have any number of devices in a device class (up to the total limit of devices for your DU SKU tier).
A device class can be thought of as a set of devices that are all compatible with the same updates. In practice this means they all have the same PnP modelId and compat properties. You can have any number of devices in a device class (up to the total limit of devices for your DU SKU tier).
Ah I was giving each update a unique model. I'll stop doing that then.
@matthewfarstad Is the original issues resolved and this can be closed?
@matthewfarstad Is the original issues resolved and this can be closed?
Yes I ended up getting this working.
Hello!
Not a lot of info I can find for my specific situation, but I've tried following the following guides and associated issue threads even slightly related to my issue:
https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-agent-provisioning https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-raspberry-pi
I was able to get the identity service just fine, but I can't seem to install the deviceupdate-agent from any apt packages, and I really want to verify there's no other way before trying to compile myself. I've added the microsoft-prod.list for debian 11 and have also apt installed the packages-microsoft-prod.deb for debian 11. I've tried debian 10 and stretch/multiarch and even a couple of ubuntu images for fun. Not able to find any deviceupdate-agent. The microsoft-prod.list file has arm64 as a supported architecture.
Essentially, what are my options? Can't change distros or architectures, ideally this all runs on this debian 11 arm64 SOM.