Closed longview closed 1 year ago
You should be able to follow this method to do what you need to.
If you are doing this for the plutoplus
, then you will need to use the attached its
file, the one linked to in the repo is that for PlutoSDR
. plutoplus.zip
BTW, I found that the process works best using root
privileges on the machine you use to do the firmware modifications on.
You can extract, modify (eg, add required scripts and executable binaries) and then re-pack the rootfs
. You will need to go through the process every time you need to do a change. AFAIK there are no "user" nv settings other than those described here . I suspect that it would be possible to add a fw_setenv
type variable to allow limited startup options without the need to reload firmware, but I have not looked into that as yet.
I have used an init.d
script (placed rootfs/etc/init.d/
) to auto-run an application at startup. Something like this should serve as a rough guide.
Thanks for getting back to me so soon!
I definitely made some mistakes the first time around, and didn't catch the different .its file, I also didn't run the cpio extract as root, so some files were probably missing when it was rebuilt.
But I got my build working now. And by modifying rcS to also search /mnt/jffs2/etc/init.d I can do what I want. /mnt/jffs2 is a small non-volatile but writable partition so I'm surprised ADI didn't add this by default.
I am pleased you came right, would you kindly post or send me the modified rcS
, I will then rebuild the current and make sure it is included in any future .frm
's.
I'll just post what I added, it's a duplicate of what's already there but searches a different path:
# append to /etc/init.d/rcS to execute user services after main init
for i in /mnt/jffs2/etc/init.d/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
done
This doesn't seem to cause any issues with booting if that additional init.d folder is not present.
I guess some users may not have the partition, looking at the ADI wiki: https://wiki.analog.com/university/tools/pluto/users/customizing#enabling_persistent_ssh_keys the built in command device_format_jffs2
can be used to activate it.
Also for anyone reading this in the future I'll note that the files in /mnt/jffs2/etc/init.d have to be executable and follow the Snn... naming convention, but they do not actually have to be formatted like service control scripts (it's just better to do so).
So for example I added an additional "service" to fix up the network configuration when using a static IP to e.g. make ntpd work and use my local ntp servers:
echo "nameserver 192.168.1.1" > /etc/resolv.conf
route add default gw 192.168.1.1 eth0
#echo "server <user ntp server>">>/etc/ntp.conf
#/etc/init.d/S49ntp restart
So that's a simple way of just executing a few commands on startup.
An additional thought I had is that the jffs2 partition is not cleared out even when doing a DFU update.
There is therefore a risk that adding a broken init script there could brick the device, and even a DFU update wouldn't restore it (if the DFU'd image also checks for the jffs inits).
DFU update does seem to clear the firmware parameters, so a ideally it should check some firmware parameter before executing the jffs init scripts. That way a DFU recovery would be a simple way to disable the extra init scripts.
Thank you, but should /etc/init.d/rcK
not also be similarly modified ?
Also, would it not be more consistent to rewrite the script's rcS
and rcK
so that the perspective Snn files are run in numerical nn
order /etc/init.d/Snn
vs /mnt/jffs2/etc/init.d/Snn
with priority given to those in /etc/init.d
?
rcK is not something I concerned myself with since nothing I run on the Pluto requires a clean stop, but it should probably be modified similarly yes.
I modified the above slightly to check for the firmware variable localinit:
LOCALINIT=`fw_printenv -n localinit`
if [ "$LOCALINIT" == "true" ]
then
for i in /mnt/jffs2/etc/init.d/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
done
fi
Then it can be activated by running fw_setenv localinit true
and disabled by e.g. setting fw_setenv localinit ""
or setting it to any other value. This should be cleared by DFU updates but not normal ssh or USB mass storage updates, allowing for relatively simple recovery in case someone messes up.
Tested with values other than 'true' and by clearing the variable and both those cases don't execute the local scripts.
To run the new scripts in order we could simply copy /mnt/jffs/etc/init.d/*
to /etc/init.d
right at the start?
This would allow full replacement of the init scripts as needed, though it would prioritize the users scripts since those could overwrite the firmware scripts.
I suspect it's probably not strictly necessary to have this level of control though, running the user inits last is likely to be what most people want anyway I think?
I suspect it's probably not strictly necessary to have this level of control though, running the user inits last is likely to be what most people want anyway I think?
Agreed, anyone who wants something more complex than what is on offer could just modify the firmware themselves.
I think what you have suggested makes sense. Please check the modified scripts below, I changed the name of the variable to make it a little more descriptive (at least to me).
/etc/init.d/rcS
#!/bin/sh
# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
done
INIT_LOCAL=`fw_printenv -n init_ext_jffs2`
if [ "$INIT_LOCAL" == "true" ]
then
for i in /mnt/jffs2/etc/init.d/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
done
fi
/etc/init.d/rcK
#!/bin/sh
# Stop all init scripts in /etc/init.d
# executing them in reversed numerical order.
#
for i in $(ls -r /etc/init.d/S??*) ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set stop
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i stop
;;
esac
done
KILL_LOCAL=`fw_printenv -n init_ext_jffs2`
if [ "$KILL_LOCAL" == "true" ]
then
for i in $(ls -r /mnt/jffs2/etc/init.d/S??*) ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set stop
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i stop
;;
esac
done
fi
First of all thank you for taking on the burden of updating this! Really appreciated.
It would be super nice to have a way to execute a user script on startup, stored in the jffs2 partition. This way simple things could be replaced at run-time without rebuilding the entire rootfs image. (Also, I tried a rebuild according the instructions in the other repo you have and something broke so had to DFU recovery)
Perhaps one way to do it would be to have /etc/rcS also call a folder in /mnt/jffs2/etc/init.d? That way users could add or remove service control scripts as needed, but simply calling a shell script would also be fine and maybe easier for non-experts to set up.
My use case is kicking off a very simple script that reads the receiver frequency via the sysfs and controls an external filter-bank/antenna switcher over bit-bashed SPI. Working quite well, but for now I have to scp and run them manually on every reboot.