Debug custom image built with imagebuilder (uci-defaults)

Hi there!

After a couple of years updating my OpenWRT powered routers by hand (i.e. sysupgrade, reinstall packages and fix things in case something broke), I want to finally set up a process which is more streamlined and reliable. I want to build a self-contained image (incl. all packages I need plus configuration) for each new release and flash it on my router, and in case something is not working right I want to simply flash the old image over again (while working with the new image on a separate device to get things going). Doing a little research, I found Image Builder and thought this is perfect for what I'm aiming for.

I got started and built custom images with packages and my config files which worked great, but then stumbled upon the note in the "Custom files" section that recommends using uci-defaults for setting config settings. I extracted the config settings I had in my /etc/config files and put them into a uci-defaults script, rebuilt and flashed the image but my router did not want to come up. I had to boot into recovery mode and flash with the stock image to get it to work again.

I'm pretty sure the problem is caused by my custom uci-defaults script, but I do not have an idea where to get started to debug this issue. Do you have any tips for me?

Here a (stripped down) version of my custom uci-defaults script:

#!/bin/sh

uci -q batch << EOI
dhcp.@dnsmasq[0].server='**********'
dhcp.lan.dhcp_option='6,**********'
dhcp.wan.ignore='1'
dhcp.wan.ra_flags='none'

dhcp.@host[0]=host
dhcp.@host[0].name='**********'
dhcp.@host[0].mac='**********'
dhcp.@host[0].ip='**********'
dhcp.@host[0].dns='1'

dhcp.@host[1]=host
dhcp.@host[1].name='**********'
dhcp.@host[1].mac='**********'
dhcp.@host[1].ip='**********'
dhcp.@host[1].dns='1'

commit dhcp
EOI

uci -q batch << EOI
dropbear.@dropbear[0].Interface='lan'
commit dropbear
EOI

uci -q batch << EOI
fstab.@global[0].anon_mount='1'

fstab.@mount[0]=mount
fstab.@mount[0].target='/mnt/sda1'
fstab.@mount[0].uuid='5c9c01e2-21f5-4d3f-a883-f2ea39596192'
fstab.@mount[0].enabled='1'

commit fstab
EOI

uci -q batch << EOI
luci_statistics.collectd_rrdtool.DataDir='/mnt/sda1/rrd'
luci_statistics.collectd_rrdtool.CacheTimeout='300'
luci_statistics.collectd_rrdtool.CacheFlush='1500'

luci_statistics.collectd_disk.enable='1'
luci_statistics.collectd_disk.Disks='sda1'

luci_statistics.collectd_interface.enable='1'
luci_statistics.collectd_interface.Interfaces='eth0' 'eth1' 'eth1.1'
luci_statistics.collectd_interface.IgnoreSelected='1'

luci_statistics.collectd_ping.enable='1'
luci_statistics.collectd_ping.Hosts='**********' '**********'
luci_statistics.collectd_ping.AddressFamily='any'

luci_statistics.collectd_thermal.enable='1'
luci_statistics.collectd_thermal.Device='cooling_device0' 'cooling_device1' 'cooling_device2' 'cooling_device3'
luci_statistics.collectd_thermal.IgnoreSelected='1'

commit luci_statistics
EOI

uci -q batch << EOI
network.globals.ula_prefix='**********'
network.wan.peerdns='0'
commit network
EOI

uci -q batch << EOI
system.@system[0].hostname='**********'
system.@system[0].log_proto='udp'
system.@system[0].conloglevel='8'
system.@system[0].cronloglevel='5'
system.@system[0].zonename='Europe/Vienna'
system.@system[0].timezone='CET-1CEST,M3.5.0,M10.5.0/3'

commit system
EOI

uci -q batch << EOI
delete wireless.default_radio0
delete wireless.default_radio1

wireless.radio0=wifi-device
wireless.radio0.type='mac80211'
wireless.radio0.path='soc/1b500000.pci/pci0000:00/0000:00:00.0/0000:01:00.0'
wireless.radio0.channel='36'
wireless.radio0.band='5g'
wireless.radio0.htmode='VHT80'
wireless.radio0.cell_density='0'

wireless.radio1=wifi-device
wireless.radio1.type='mac80211'
wireless.radio1.path='soc/1b700000.pci/pci0001:00/0001:00:00.0/0001:01:00.0'
wireless.radio1.channel='1'
wireless.radio1.band='2g'
wireless.radio1.htmode='HT20'
wireless.radio1.cell_density='0'

wireless.wifinet2=wifi-iface
wireless.wifinet2.device='radio0'
wireless.wifinet2.mode='ap'
wireless.wifinet2.ssid='**********'
wireless.wifinet2.encryption='sae-mixed'
wireless.wifinet2.key='**********'
wireless.wifinet2.network='lan'
wireless.wifinet2.ifname='wlan0-0'

wireless.wifinet1=wifi-iface
wireless.wifinet1.device='radio0'
wireless.wifinet1.mode='ap'
wireless.wifinet1.ssid='**********'
wireless.wifinet1.encryption='sae-mixed'
wireless.wifinet1.key='**********'
wireless.wifinet1.network='lan'
wireless.wifinet1.ifname='wlan0-1'

wireless.wifinet3=wifi-iface
wireless.wifinet3.device='radio1'
wireless.wifinet3.mode='ap'
wireless.wifinet3.ssid='**********'
wireless.wifinet3.encryption='sae-mixed'
wireless.wifinet3.hidden='1'
wireless.wifinet3.key='**********'
wireless.wifinet3.network='lan'
wireless.wifinet3.ifname='wlan1-0'

wireless.wifinet4=wifi-iface
wireless.wifinet4.device='radio1'
wireless.wifinet4.mode='ap'
wireless.wifinet4.ssid='**********'
wireless.wifinet4.encryption='sae-mixed'
wireless.wifinet4.key='**********'
wireless.wifinet4.network='lan'
wireless.wifinet4.ifname='wlan1-1'

wireless.wifinet5=wifi-iface
wireless.wifinet5.device='radio1'
wireless.wifinet5.mode='ap'
wireless.wifinet5.ssid='**********'
wireless.wifinet5.encryption='psk-mixed'
wireless.wifinet5.key='**********'
wireless.wifinet5.network='lan'
wireless.wifinet5.ifname='wlan1-2'

delete wireless.radio0.disabled
delete wireless.radio1.disabled

commit wireless
EOI

Thanks in advance for any hints and tips!

Cheers,
Chris

I do not use such custom uci-defaults script to override the original defaults, but I modify all source code directly.

E.g. mod package/kernel/mac80211/files/lib/wifi/mac80211.sh for WiFi default settings.

Thanks for your input! You build your custom image from OpenWRT's sources I guess? Seems Image Builder does not allow that - at least I wouldn't have found a file named mac80211.sh in the Image Builder files.

Seems I forgot the set keyword in front of all settings... Need to fix that and try again. In any case, I would be interested to know what my malformed uci-defaults script caused so that the device could not boot and how to debug such issues. Any ideas?

That uppercase I should not be there.

Best way to further debug is to modularise. I have multiple UCI defaults scripts, split up by functionality or by subset they touch. I'd suggest you do the same, then gradually add them to your image one by one and see where it breaks.

I found shellcheck to be very handy too to catch finger mistakes etc.

1 Like

Good point, will split up the script into multiple ones. I was wondering myself on the upper case "I", but this seems to be correct: https://openwrt.org/docs/guide-user/base-system/dropbear#dropbear, no?

Any idea if there is some kind of log or similar I can check in failsafe mode that can give me hints what failed?

You're right, my bad. Other settings ate uppercased as well.

Another approach btw is to run all the commands from your script manually, one by one, and check where you get stuck.

No problem at all, thanks for your input!

I tried executing the script after I flashed the stock image over - script did not provide any output or error code which was very strange for me. Therefore, I'm not so sure whether my script per se is the problem, or whether I set some config setting causing the device to not boot.

One problem I have is that I do not know what UCI config settings are already set at the time the uci-defaults scripts run, so I do not know whether I can simply set or have to add first...

I have been doing what you are trying to do for over a year and it works great: a self configuring image is great a time saver. Here are a few of suggestions from my experience:

  1. I find batch more difficult to debug. Set each value independently and when something goes wrong, you can tell which line failed:
uci set fstab.@global[0].auto_swap="0"
uci set fstab.@global[0].auto_mount="0"
uci set fstab.@global[0].check_fs="1"
uci -q del fstab.@mount[0] || true   # get rid of default config if any
uci add fstab mount >/dev/null
uci set fstab.@mount[0].enabled="1"
uci set fstab.@mount[0].fstype="ext4"
uci set fstab.@mount[0].options="noatime,nodiratime"
uci set fstab.@mount[0].target="/mnt/data"
uci set fstab.@mount[0].enabled_fsck="1"
uci set fstab.@mount[0].uuid="blah"
uci commit
  1. Debug by making small incremental changes at a time: the code above is from my script, so you can just copy and paste it. It should just work
  2. Run your script manually with sh +x -e script.sh to get debugging information
  3. If something goes wrong, run firstboot followed by reboot to reset the router
  4. Once you have it all running just like you wanted, add a line exec >> /root/_log.setup 2>&1 after the very first line of your script. This way you will always fave a log file in /root/_log.setup and you can inspect it later.

About default config settings: you can create an image without your scripts and copy /etc/confg OR you can make it a part of your script:

At the beginning:

#!/bin/sh -e
cp -r /etc/config /root/_config.original

and at the end:

cp -r /etc/config /root/_config.running

This way you will always have the original and running config.

Let me know if you get stuck making some specific changes and I will share parts of the script I am using.

1 Like
	-q         quiet mode (don't print error messages)
1 Like

Thanks for these tips - they helped me to get a working version. What was key for me:
*) Introduce some log messages and write all output of the script to a file under /root (exec >/root/uci-defaults.log 2>&1)
*) Split up the setup into multiple scripts
*) Use single uci commands (vs. uci batch)

Here an example - in case someone stumbles upon this thread:

#!/bin/sh -e

exec >/root/uci-defaults.log 2>&1

echo 'Run custom setup script for `system` config'

echo '-- config before:'
uci show system
echo

uci set system.@system[0].hostname='****'
uci set system.@system[0].conloglevel='7'
uci set system.@system[0].cronloglevel='8'
uci set system.@system[0].log_file='/mnt/sda1/system.log'
uci set system.@system[0].log_size='1024'

uci commit system

echo '--  config after:'
uci show system
echo

echo
exit 0
1 Like

Just found this old thread when looking for more information about uci-defaults, as the wiki page is rather short.

When using multiple scripts, as suggested, how do you number them in the uci-defaults folder? Like from 90-99, or just all 99-contextname?

Also, shouldn't there be #!/bin/sh -ex to enable printing of executed commands for the logfile and testing runs of the script(s)?

They are sorted alphabetically: take a look at the current scripts on an installed router (under /rom/etc/uci-defaults) as well as at the script that executes them. That should answer most of your questions.

Thanks, will do.

Was actually hoping to find more examples from other people in this thread, like you or @ckristo.

Do you happen to have them on Github or something similar, or do they contain too much sensitive information?

I created scripts per config section named 99-custom-<nr>_<configSection>, e.g., 99-custom-01_system. Each script has the same structure - here the structure for the system config section:

#!/bin/sh -e

exec >/root/uci-defaults.log 2>&1

echo 'Run custom setup script for `system` config'

echo '-- config before:'
uci show system
echo

uci set system.@system[0].hostname='OpenWRT'
# ... other `uci set` commands follow here ...

uci commit system

echo '--  config after:'
uci show system
echo

echo
exit 0

Hope you'll find this useful.

1 Like