Run my custom command on every uci commit

Hello!

I'm looking for a way for make the system invoke my custom command after each uci commit (including commits issued by luci). Couldn't find a way to do it.

Is there an already implemented way to achieve this?

If not, a good idea would be to have a ubus event for uci commit - then I'd be able to wait for in a loop in a bash script and invoke the my command.

My end goal is to push a backup of all the configuration files to an external server on every change - so I also looked into things like alternative uci backends, but that seems to be too heavy of a solution for the task this simple.

mkdir -p /etc/profile.d
cat << "EOF" > /etc/profile.d/uci.sh
uci() { /sbin/uci "${@}"; if echo "${@}" | grep -q "commit"; then backup; fi }
EOF

This is not what I need, as it only affects uci commit invocations made via cli using shell. I essentially want to capture any configuration change - including luci and ubus-issued changes.

Try to override uci binary with a wrapper.
It seems LuCI uses ubus:

I'm pretty sure any alterations/wrapping of the uci executable are not going to help, and neither they are really the architecturally correct way of doing what I need. This is because I technically want the system to react to the the commit event itself, and not merely the invocation on uci commit binary. My current research of the source of uci/ubus suggests that the actual commit event can be issued by means other than invoking the uci executable.

To be more specific, all that uci binary does is it invokes libuci internally (uci_commit for commit), and any other code can do that too.

This is why what you're suggesting is not going to work at all, as it only wraps around one way of doing the commit, but I want to capture all of them.

1 Like

After more research of the uci source code, it seems there is currently no way of doing what I need.
So, I'd like to discuss possible additions to the uci to enable dispatching events for other parts of the system to subscribe to.

On the second though, it seems rpcd posts an event:

https://git.openwrt.org/?p=project/rpcd.git;a=blob;f=uci.c;h=1587a197a2bd6eb07345c7d21803b15143fa58e2;hb=HEAD#l1342

I do not consider any extension of this uci/ubus stuff a good idea.
Simply because it enhances complexity, coupled with "foggy" documentation, politely said. KISS is my favourite method.

Event driven processing is prone to lost events, for which some backup procedure only guarantees real production stability. Which usually is a timer based polling.

For the outlined requirement, a cron job, once a minute, should suffice, to check last mods of files in /etc/config. Or use a small shell script with a sleep between checks.

2 Likes

I agree that something like rsync is probably a robust and efficient approach. You might make it more sophisticated by checking modification times with find and/or additionally triggering with events.

Edit:

The inotify functionality may also be valuable to explore. As I understand it, it can trigger events on file changes. I don't know if it is available as a drop-and-go addition to an OpenWrt install, nor if it can easily be configured to handle additions of new files to subdirectories.

The problem is losing the changes that happen between the backups.

The situation is there may be nothing happening for months, and then something happends that immediately breaks the system, in which case we'd want to catch both states before and after the uci commit. Ideally we'd want to have a hook to run a command before commit, as well as after commit, but capturing stuff aonly after the commit works for me too at this point.

I'll have to check if doing the rsync frequent enough to tho what I need is feasible.

Not an easy problem if you're capturing the information off-device. "Joe" changes the LAN address from 192.168.0.22 and "derps" to 192.169.0.23 instead of 192.168.0.23. Poof, no way to connect to the remote. It might even be that a config file was edited and the device comes up with "bad" config. Or "Bob" decides unwisely to run opkg upgrade and TLS connectivity dies.

True, this is a problem with such approach.
I was initially planning to have a (local) git repo where all the commits form uci are recorded, and then to push/pull it from time to time.

1 Like

There's one more point, which is uci commit only overwrites previous persistent configuration, it doesn't require to apply it, or you can apply changes without saving them.
So it's possible to reproduce the situation when service runtime configuration differs from UCI runtime configuration, and UCI runtime configuration differs from UCI persistent configuration.

1 Like

Yeah, for this case my the goal is to capture the persisted configs, kind of.
Maybe for the broader use cases it'd be best to alter libuci in a way that it does the proper audit + state backups, but for me just capturing commits would be good enough.

Can someone point me to where I can take a look at the code that's responsible of picking up the changes to the files at /etc/config when I alter them manually?

I'm thinking that if it's using something like inotify I could do just the same - and it will catch all cases I'm looking to cover.

AFAIK there is no "pickup of changes". The service concerned will simply use it on next start via init script for procd.

2 Likes

Oh, I was under the impression that it was a thing, probably I'm wrong.