Dhcp-script won't execute

Recently I upgraded from OpenWRT 21.02 to 22.03, specifically:
OpenWrt 22.03.0 r19685-512e76967f / LuCI openwrt-22.03 branch git-22.245.77528-487e58a

It seems that something has changed which broke a previously good functionality.

In /etc/dnsmasq.conf, I have a line at the bottom of the file:
dhcp-script=/root/custom-script.sh

This has reliably worked for ages. But after the recent upgrade, I see this in system logs:
Fri Oct 14 11:38:48 2022 daemon.err dnsmasq[1]: failed to execute /root/custom-script.sh: No such file or directory

My attempts to fix it have included:
Moving the script from /root to /usr/bin (and of course adjusting the references)
Changing file permissions on the script from 700 to 755
Changing file ownership from root to dnsmasq

Thus the script file looks like:
-rwxr-xr-x 1 dnsmasq dnsmasq 666 Oct 14 11:29 custom-script.sh

And even after all that, still I see the error.

Again this worked on several previous major versions of OpenWRT for the past X years. What may have changed in 22.03, and more importantly, how to fix?

Use the dhcpscript option in uci to set the path to the custom script so that the new ujail restrictions will detect it.

Maybe I didn't run the uci command correctly (I haven't used it before), as this didn't work.

uci set dhcp.dhcpscript='/usr/bin/custom-script.sh'
uci commit dhcp
reload_config

And still seeing this in logs:
Fri Oct 14 13:55:51 2022 daemon.err dnsmasq[1]: failed to execute /usr/bin/custom-script.sh: No such file or directory

Probably needs to be dhcp.@dnsmasq[0].dhcpscript='/usr/bin/custom-script.sh'

I don’t use this myself, so there’s some trial-and-error involved here. Then remove any previous config you had that would add it to dnsmasq.conf directly.

I read that I should be using the Hotplug functionality to run the script, i.e.

So I dropped a simple test script in /etc/hotplug.d/dhcp
-rw-r--r-- 1 root root 180 Oct 14 15:58 98-something

With contents:

#!/bin/sh

echo "hello world from something $1"

if [ "$ACTION" = add ] ; then
(
  echo "just did an add"
)
fi

if [ "$ACTION" = update ]; then
(
  echo "just did an update"
)
fi

This is not being executed.

Does anybody know how to successfully run a script that is triggered by a dhcp event?

As mentioned in the OP, this functionality worked fine until I upgraded OpenWRT to version 22.03.

have you tried what @dave14305 suggested? because he is right. if you are uncomfortable with uci commands you can simple add the required setting to /etc/config/dhcp in the format of option dhcpscript '/path/to/your.script.sh' under config dnsmasq. set executable bit too on your script.

Do you mind to point me on that update? What have I missed? Aka Can you explain, please?

grrr2,

I followed those instructions so the /etc/config/dhcp file contains this:

config dnsmasq
        option domainneeded '1'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option localservice '1'
        option port '53'
        option cachesize '500'
        option filterwin2k '1'
        option dhcpscript '/root/custom-script.sh'

I then ran the command: /etc/init.d/dnsmasq restart

But the script is not being executed. This time there is no error message in the system log; it seems that the service is not even attempting to run the script. Originally it was attempting to run it but giving the error message.

Still this hotplug documentation leads me to think that it is the right way to go:

But that is not working either. At what point should I file bug reports for the developers?

Does anyone have a working example of dhcp event scripts running on OpenWRT 22.03 ?

It calls the script but the script is still limited in what it can run by the ujail.

# cat /root/custom-script.sh 
#!/bin/sh

echo "$0" "My custom script called with $@"
# uci show dhcp | grep root
dhcp.@dnsmasq[0].dhcpscript='/root/custom-script.sh'
# grep dhcp-script /var/etc/dnsmasq.conf.cfg01411c 
dhcp-script=/usr/lib/dnsmasq/dhcp-script.sh
# head /usr/lib/dnsmasq/dhcp-script.sh 
#!/bin/sh

[ -f "$USER_DHCPSCRIPT" ] && . "$USER_DHCPSCRIPT" "$@"
Sat Oct 15 09:35:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh My custom script called with old d4:68:xxx 192.168.1.169 mydevice
Sat Oct 15 09:35:58 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh My custom script called with arp-del d4:68:xxx 2601:xxx
Sat Oct 15 09:35:58 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh My custom script called with arp-add d4:68:aa:xxx 2601:xxx

can you please copy-paste your generated ( ! ) dnsmasq config file?

# ps | grep dnsm
 4416 root      2160 S    {dnsmasq} /sbin/ujail -t 5 -n dnsmasq -u -l -r /bin/ubus -r /etc/TZ -r /etc/dnsmasq.conf -r /etc/ethers -r /etc/group -
 4417 dnsmasq   1180 S    /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmasq.cfg01411c.pid

# cat  /var/etc/dnsmasq.conf.cfg01411c

# your config file name might be different, so whatever after -C is the generated config file

the hotplug method works as follows:
using the same dhcpscript option and pointing to /usr/lib/dnsmasq/dhcp-script.sh. this script fires the hotplug events so whatever you put under /etc/hotplug.d/dhcp will be executed with some variables passed.
easiest way to see what variables are passed to use env command imho.

I tried this in my test above but the env command wasn’t accessible to the script, so I went with an echo.

could you try with this?

# in /usr/lib/dnsmasq/dhcp-script.sh change

from
[ -f "$USER_DHCPSCRIPT" ] && . "$USER_DHCPSCRIPT" "$@"

to
[ -f "$USER_DHCPSCRIPT" ] && [ $USER_DHCPSCRIPT != $0 ] && . "$USER_DHCPSCRIPT" "$@"

and create two hotplug scripts one under /etc/hotplug.d/neigh other under /etc/hotplug.d/dhcp . then restart dnsmasq.

The hotplug scripts work fine, but I didn’t see the point of modifying dhcp-script.sh since custom-script.sh would not equal dhcp-script.sh.

@dmw73 What does your custom script try to do? It will probably be best to only use the hotplug method, depending what your script wants to achieve.

sorry i think i misunderstood something.

so yes, you are right the hotplug works as should: no need to do any magic, just add some script file under either /etc/hotplug,d/[dhcp|neigh|tftp] and dnsmasq init will pick it up and create necessary config.

hence i asked OP to show the generated dnsmasq config to verify if /usr/lib/dnsmasq/dhcp-script.sh is added to config or not automatically.

if it is there then OP's script might cause the problem, so that should be the next step to verify: what it is supposed to do versus what it is actually doing.

1 Like

(1) Hotplug method. This is what I have:

root@myhost:/etc/hotplug.d/dhcp# cat 98-something 
#!/bin/sh

echo "hello world from something $1"

if [ "$ACTION" = add ] ; then
(
  echo "just did an add"
)
fi

if [ "$ACTION" = update ]; then
(
  echo "just did an update"
)
fi

According to documentation, this should be working, but nothing happens.

(2) The generated dnsmasq.conf file

root@myhost:/etc/config# cat /var/etc/dnsmasq.conf.cfg01411c 
# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
dhcp-authoritative
domain-needed
filterwin2k
localise-queries
read-ethers
enable-ubus=dnsmasq
expand-hosts
bind-dynamic
local-service
cache-size=500
port=53
domain=lan
local=/lan/
addn-hosts=/tmp/hosts
dhcp-leasefile=/tmp/dhcp.leases
dhcp-script=/usr/lib/dnsmasq/dhcp-script.sh
script-arp
resolv-file=/tmp/resolv.conf.d/resolv.conf.auto
stop-dns-rebind
rebind-localhost-ok
dhcp-broadcast=tag:needs-broadcast
conf-dir=/tmp/dnsmasq.d
user=dnsmasq
group=dnsmasq

(3) The contents of /usr/lib/dnsmasq/dhcp-script.sh (none of it is my custom script; I don't know where any of this comes from)

root@myhost:/usr/lib/dnsmasq# cat /usr/lib/dnsmasq/dhcp-script.sh 
#!/bin/sh

[ -f "$USER_DHCPSCRIPT" ] && . "$USER_DHCPSCRIPT" "$@"

. /usr/share/libubox/jshn.sh

json_init
json_add_array env
hotplugobj=""

case "$1" in
        add | del | old | arp-add | arp-del)
                json_add_string "" "MACADDR=$2"
                json_add_string "" "IPADDR=$3"
        ;;
esac

case "$1" in
        add)
                json_add_string "" "ACTION=add"
                json_add_string "" "HOSTNAME=$4"
                hotplugobj="dhcp"
        ;;
        del)
                json_add_string "" "ACTION=remove"
                json_add_string "" "HOSTNAME=$4"
                hotplugobj="dhcp"
        ;;
        old)
                json_add_string "" "ACTION=update"
                json_add_string "" "HOSTNAME=$4"
                hotplugobj="dhcp"
        ;;
        arp-add)
                json_add_string "" "ACTION=add"
                hotplugobj="neigh"
        ;;
        arp-del)
                json_add_string "" "ACTION=remove"
                hotplugobj="neigh"
        ;;
        tftp)
                json_add_string "" "ACTION=add"
                json_add_string "" "TFTP_SIZE=$2"
                json_add_string "" "TFTP_ADDR=$3"
                json_add_string "" "TFTP_PATH=$4"
                hotplugobj="tftp"
        ;;
esac

json_close_array env

[ -n "$hotplugobj" ] && ubus call hotplug.${hotplugobj} call "$(json_dump)"

(4) The original script I had been using -- which worked fine in previous versions of OpenWrt -- just sends me an email when a new WiFi connection is established. It looks like this:

root@myhost:~# cat custom-script.sh 
#!/bin/sh

# script to detect new dhcp lease

# this will be called by dnsmasq everytime a new device is connected
# with the following arguments
# $1 = add | old
# $2 = mac address
# $3 = ip address
# $4 = device name

notification_email="xxxxxxxxxxxx"

if [ "$1" == "add" ]; then
  txt="New device on `uci get system.@system[0].hostname`.`uci get dhcp.@dnsmasq[0].domain` $*"
  msg=$(echo "$txt" | sed s/:/-/g)
  echo `date` $msg >> /tmp/dhcpmasq.log

  # encode colon (:) and send email
  # echo $msg | sed s/:/-/g | msmtp "$notification_email"
  printf "Subject: WiFi Alert\n$msg" | msmtp "$notification_email"
 
fi

Recall if you will the error message was this:
Fri Oct 14 11:38:48 2022 daemon.err dnsmasq[1]: failed to execute /root/custom-script.sh: No such file or directory

I think I addressed all the comments from dave14305 and grr2, did I miss anything? Anyway, thank you guys so much for trying to help solve this, I am very appreciative. I rely on this notification tremendously, it has proven very valuable over the years with knowing who is on the property!

For the hotplug testing, use logger instead of echo. For your original notification script, you would have to incorporate the ACTION, IPADDR, MACADDR and HOSTNAME variables instead of positional parameters ($1 or $*).

if [ "$ACTION" = "add" ]; then
  txt="New device on $(uci get system.@system[0].hostname).$(uci get dhcp.@dnsmasq[0].domain) $MACADDR $IPADDR $HOSTNAME"
1 Like

Following Dave's advice, I made a hotplug script as such:

root@myhost:/etc/hotplug.d/dhcp# cat 97-something 
#!/bin/sh

logger "hello logger from something with action=$ACTION"


if [ "$ACTION" = "add" ] ; then
(
  logger "just did an add"
)
fi

if [ "$ACTION" = "update" ]; then
(
  logger "just did an update"
)
fi

After restarting dnsmasq, and connecting a device on WiFi, I see this in the log:

Mon Oct 17 09:24:36 2022 user.notice root: hello logger from something with action=update
Mon Oct 17 09:24:36 2022 user.notice root: just did an update

So now that that's working, I'll re-work the original email notification script. Will post an update once that is back.

Side note, I noticed that in /etc/hotplug.d/ there are some existing scripts, for example:

root@DeerHerd:/etc/hotplug.d# ls -al /etc/hotplug.d/ntp/25-dnsmasqsec 
-rw-r--r--    1 root     root           277 Sep  2 22:55 /etc/hotplug.d/ntp/25-dnsmasqsec

I am surprised these work given the file permissions do not include executable.

Scripts in /etc/hotplug.d/ are sourced by the /sbin/hotplug-call script, they're not executed.

Posting for posterity. This is what works for email notifications of WiFi lease events:

root@myhost:/etc/hotplug.d/dhcp# pwd
/etc/hotplug.d/dhcp
root@myhost:/etc/hotplug.d/dhcp# ls -al
drwxr-xr-x    1 root     root             0 Oct 17 10:18 .
drwxr-xr-x    1 root     root             0 Oct 14 18:44 ..
-rw-------    1 root     root           499 Oct 17 11:23 98-custom-script

contents of 98-custom-script

#!/bin/sh

notification_email="destination_email@somewhere"

if [ "$ACTION" = "add" ] || [ "$ACTION" = "update" ]; then
# if [ "$ACTION" = "add" ]; then
(
  tx1="WiFi $ACTION lease on $(uci get system.@system[0].hostname).$(uci get dhcp.@dnsmasq[0].domain)\r\n$MACADDR\r\n$IPADDR\r\n$HOSTNAME"
  tx2="$(echo $tx1 | sed s/:/-/g)"
  logger "DEBUG WiFi notification message: $tx2"
  printf "Subject:  WiFi Alert \r\n\r\n`date`\r\n$tx2" | msmtp "$notification_email"
)
fi

And of course the prerequisite is having msmtp set up on the router too.

Thanks guys for your help in getting this sorted out.

dmw73

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.