Dnsmasq dhcp hotplug script not running

I am running openwrt latest version on Mi Wifi Mini router.
I have setup a hotplug script under directory /etc/hotplug.d/dhcp/00-detect_new_device.sh

The script is running successfully on a different router (Archer A7v5)

On this router,
no matter what script i place under /etc/hotplug.d/dhcp/ directory, i get the same error in system log,

Wed Nov 2 19:52:59 2022 daemon.debug dnsmasq-script[1]: Command failed: Not found
Wed Nov 2 19:52:59 2022 daemon.warn dnsmasq[1]: script process exited with status 4

Even if i place a blank file in that directory, it shows the same error.
But there's no error if i remove all files under /etc/hotplug.d/dhcp/ directory.

My dnsmasq version: 2.86-14

Contents of /etc/dnsmasq.conf

# Change the following lines if you want dnsmasq to serve SRV
# records.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>

# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389

# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2

# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com

# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD.
# The fields are <name>,<target>
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"

# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf.
# The fields are <name>,<text>,<text>...

#Example SPF.
#txt-record=example.com,"v=spf1 a -all"

#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4

# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand
# The fields are <cname>,<target>
#cname=bertand,bert
#dhcp-relay=172.26.31.100,172.26.31.200
#dhcp-script=/etc/hotplug.d/dhcp/98-something.sh

Contents of /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option localise_queries '1'
        option local '/lan/'
        option expandhosts '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option ednspacket_max '1232'
        option authoritative '1'
        option sequential_ip '1'
        list server '172.26.31.1'
        list server '8.8.8.8'
        list server '8.8.4.4'
        option localservice '0'
        option rebind_protection '0'

config dhcp 'lan'
        option interface 'lan'
        option dhcpv4 'server'
        option start '2'
        option limit '254'
        option leasetime '72h'
        option force '1'
        option netmask '255.255.255.0'
        list dhcp_option '6,8.8.8.8,8.8.4.4'
        list dhcp_option '3,172.26.31.1'

config dhcp 'wan'
        option interface 'wan'
        option ignore '1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/hosts/odhcpd'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'

config dhcp 'GuestWifi'
        option interface 'GuestWifi'
        option leasetime '2h'
        option force '1'
        list dhcp_option '6,8.8.8.8,8.8.4.4'
        option start '2'
        option limit '50'
        option netmask '255.255.255.0'
        list ra_flags 'none'

config dhcp 'wwan'
        option interface 'wwan'
        option start '2'
        option limit '254'
        option leasetime '72h'
        option force '1'
        list dhcp_option '3,172.26.31.1'
        list dhcp_option '6,8.8.8.8,8.8.4.4'
        option netmask '255.255.255.0'

Contents of /var/etc/dnsmasq.conf.cfg04411c

# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
dhcp-authoritative
domain-needed
localise-queries
read-ethers
enable-ubus=dnsmasq
expand-hosts
bind-dynamic
dhcp-sequential-ip
edns-packet-max=1232
local=/lan/
server=172.26.31.1
server=8.8.8.8
server=8.8.4.4
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
dhcp-broadcast=tag:needs-broadcast
conf-dir=/tmp/dnsmasq.d
user=dnsmasq
group=dnsmasq

I have omitted some contents containing mac addresses of my devices.

I have exactly the same problem on my rpi4 with openmptcprouter v0.59.1-5.4 r0+16594 and dnsmasq version 2.85
Have you managed to solve this problem?

yes actually.....sorry, didn't update here...
issue was permissions of my dhcp directory in /etc/hotplug.d/dhcp

i resolved it by just deleting the dhcp directory...and on restart, it was created again with correct permissions....then i just placed my script in there again, and it worked...

This doesn't work in my case. Can you recall what was the permissions before you delete the directory and what are they now?

in my case it looks like this:

root@OpenMPTCProuter:~# ls -l /etc/hotplug.d/
drwxr-xr-x    2 root     root            54 May 18 01:11 block
drwxr-xr-x    2 root     root             3 May 18 01:11 dhcp
drwxr-xr-x    2 root     root            37 May 18 01:11 ieee80211
drwxr-xr-x    2 root     root           192 May 18 01:11 iface
drwxr-xr-x    2 root     root             3 May 18 01:11 neigh
drwxr-xr-x    1 root     root          3488 Sep 23 09:19 net
drwxr-xr-x    2 root     root            54 May 18 01:11 ntp
drwxr-xr-x    2 root     root            30 May 18 01:11 openvpn
drwxr-xr-x    2 root     root             3 May 18 01:11 tftp
drwxr-xr-x    2 root     root            55 May 18 01:11 tty
drwxr-xr-x    2 root     root            79 May 18 01:11 usb
drwxr-xr-x    2 root     root            33 May 18 01:11 usbmisc
drwxr-xr-x    2 root     root            43 May 18 01:11 wwan
root@OpenMPTCProuter:~#

I tried to chmod 0777 /etc/hotplug.d/dhcp No result.

I also have the same permissions...

drwxr-xr-x   10 root     root           120 Oct 15 04:14 .
drwxr-xr-x    1 root     root             0 Nov  7 17:42 ..
drwxr-xr-x    2 root     root             3 Oct 15 04:14 dhcp
drwxr-xr-x    2 root     root            63 Oct 15 04:14 firmware
drwxr-xr-x    2 root     root            85 Oct 15 04:14 ieee80211
drwxr-xr-x    2 root     root           105 Oct 15 04:14 iface
drwxr-xr-x    2 root     root             3 Oct 15 04:14 neigh
drwxr-xr-x    2 root     root            62 Oct 15 04:14 net
drwxr-xr-x    2 root     root            36 Oct 15 04:14 ntp
drwxr-xr-x    2 root     root             3 Oct 15 04:14 tftp

I recommend, first try with a basic script...that just does "hello world" in system log....echo doesn't work....you will have to use logger to output to system log...
if that works...then your script is the issue...
if that doesn't work....and you have checked the permissions is correct....then simplest would be to just backup config, and reset all settings....then try after restore...

also, i am running the full version of wpad...maybe that has something to do with it...

It does not work at all. If I put anything in /etc/hotplug.d/dhcp even an empy file, I have a log full of these lines just after restart dnsmasq service.

Sat Nov 12 18:27:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh: .: line 5: can't open '/usr/share/libubox/jshn.sh': No such file or directory
Sat Nov 12 18:27:42 2022 daemon.warn dnsmasq[1]: script process exited with status 2
Sat Nov 12 18:27:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh: .: line 5: can't open '/usr/share/libubox/jshn.sh': No such file or directory
Sat Nov 12 18:27:42 2022 daemon.warn dnsmasq[1]: script process exited with status 2
Sat Nov 12 18:27:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh: .: line 5: can't open '/usr/share/libubox/jshn.sh': No such file or directory
Sat Nov 12 18:27:42 2022 daemon.warn dnsmasq[1]: script process exited with status 2
Sat Nov 12 18:27:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh: .: line 5: can't open '/usr/share/libubox/jshn.sh': No such file or directory
Sat Nov 12 18:27:42 2022 daemon.warn dnsmasq[1]: script process exited with status 2
Sat Nov 12 18:27:42 2022 daemon.debug dnsmasq-script[1]: /usr/lib/dnsmasq/dhcp-script.sh: .: line 5: can't open '/usr/share/libubox/jshn.sh': No such file or directory
Sat Nov 12 18:27:42 2022 daemon.warn dnsmasq[1]: script process exited with status 2

Starting from scratch is a good idea, however, it is time consuming and there is no warranty, that it will help. The file in /etc/hotplug.d/dhcp that is causing problems is from mosquitto package. it is better to know what is the reason of the problem and either fix it or make a workaround. I would change back the status of the topic as it is unsolved yet.

Do you have it installed/have you tried installing jshn package?

1 Like

Can you check your /etc/dnsmasq.conf file....
if there a line
dhcp-script=/usr/share/libubox/jshn.sh
comment/remomve that...

Also,
If you don't put any file in /etc/hotplug.d/dhcp then these error don't come up on dnsmasq restart right?

problem is that /usr/lib/dnsmasq/dhcp-script.sh heavily relies on jshn package which deploys the /usr/share/libubox/jshn.sh which is missed by dhcp-script as the error message indicates. it is unlikely dhcp-script=/usr/share/libubox/jshn.sh will be found in the /etc/dnsmasq.conf (which you would not edit anyhow to have hotplug support).

as @stangri suggested please do opkg install jshn. not sure why you don't have that package installed, it is part of default package set.

1 Like

Yes, that's right

jshn is installed:

root@OpenMPTCProuter:~# opkg list-installed|grep jshn
jshn - 2021-05-16-b14c4688-2
root@OpenMPTCProuter:~#

root@OpenMPTCProuter:~# ls -l /usr/share/libubox/jshn.sh 
-rwxr-xr-x    1 root     root          5457 May 18 01:11 /usr/share/libubox/jshn.sh
root@OpenMPTCProuter:~# 

The problem is that from /usr/lib/dnsmasq/dhcp-script.sh
this line
. /usr/share/libubox/jshn.sh
can not be executed, as /usr/lib/dnsmasq/dhcp-script.sh doesn't have any access to any directory for some reason and thus can't execute /usr/share/libubox/jshn.sh which is sitting in it's place.

it is not executing but sourcing, do you see the dot at the very beginning?
it is basically trying to include content of jshn.sh.

can you copy your /etc/init.d/dnsmasq please? I'd check something.

Yes, I know the difference between these 2. In my judgement sourcing is a kind of executing, as the result of the operation depends on the contents of the file. In any case the file has to be read, which is impossible for an unknown reason.

Here it is:


root@OpenMPTCProuter:~# cat /etc/init.d/dnsmasq
#!/bin/sh /etc/rc.common
# Copyright (C) 2007-2012 OpenWrt.org

START=19

USE_PROCD=1
PROG=/usr/sbin/dnsmasq

ADD_LOCAL_DOMAIN=1
ADD_LOCAL_HOSTNAME=1
ADD_WAN_FQDN=0
ADD_LOCAL_FQDN=""

BASECONFIGFILE="/var/etc/dnsmasq.conf"
BASEHOSTFILE="/tmp/hosts/dhcp"
TRUSTANCHORSFILE="/usr/share/dnsmasq/trust-anchors.conf"
TIMEVALIDFILE="/var/state/dnsmasqsec"
BASEDHCPSTAMPFILE="/var/run/dnsmasq"
RFC6761FILE="/usr/share/dnsmasq/rfc6761.conf"
DHCPSCRIPT="/usr/lib/dnsmasq/dhcp-script.sh"

DNSMASQ_DHCP_VER=4

xappend() {
	local value="$1"

	echo "${value#--}" >> $CONFIGFILE_TMP
}

hex_to_hostid() {
	local var="$1"
	local hex="${2#0x}" # strip optional "0x" prefix

	if [ -n "${hex//[0-9a-fA-F]/}" ]; then
		# is invalid hex literal
		return 1
	fi

	# convert into host id
	export "$var=$(
		printf "%0x:%0x" \
		$(((0x$hex >> 16) % 65536)) \
		$(( 0x$hex        % 65536))
		)"

	return 0
}

dhcp_calc() {
	local ip="$1"
	local res=0

	while [ -n "$ip" ]; do
		part="${ip%%.*}"
		res="$(($res * 256))"
		res="$(($res + $part))"
		[ "${ip%.*}" != "$ip" ] && ip="${ip#*.}" || ip=
	done
	echo "$res"
}

dhcp_check() {
	local ifname="$1"
	local stamp="${BASEDHCPSTAMPFILE_CFG}.${ifname}.dhcp"
	local rv=0

	[ -s "$stamp" ] && return $(cat "$stamp")

	# If there's no carrier yet, skip this interface.
	# The init script will be called again once the link is up
	case "$(devstatus "$ifname" | jsonfilter -e @.carrier)" in
		false) return 1;;
	esac

	udhcpc -n -q -s /bin/true -t 1 -i "$ifname" >&- && rv=1 || rv=0

	[ $rv -eq 1 ] && \
		logger -t dnsmasq \
			"found already running DHCP-server on interface '$ifname'" \
			"refusing to start, use 'option force 1' to override"

	echo $rv > "$stamp"
	return $rv
}

log_once() {
	pidof dnsmasq >/dev/null || \
		logger -t dnsmasq "$@"
}

has_handler() {
	local file

	for file in /etc/hotplug.d/dhcp/* /etc/hotplug.d/tftp/* /etc/hotplug.d/neigh/*; do
		[ -f "$file" ] && return 0
	done

	return 1
}

append_bool() {
	local section="$1"
	local option="$2"
	local value="$3"
	local default="$4"
	local _loctmp
	[ -z "$default" ] && default="0"
	config_get_bool _loctmp "$section" "$option" "$default"
	[ $_loctmp -gt 0 ] && xappend "$value"
}

append_parm() {
	local section="$1"
	local option="$2"
	local switch="$3"
	local default="$4"
	local _loctmp
	config_get _loctmp "$section" "$option" "$default"
	[ -z "$_loctmp" ] && return 0
	xappend "$switch=$_loctmp"
}

append_server() {
	xappend "--server=$1"
}

append_rev_server() {
        xappend "--rev-server=$1"
}

append_address() {
	xappend "--address=$1"
}

append_ipset() {
	xappend "--ipset=$1"
}

append_interface() {
	network_get_device ifname "$1" || ifname="$1"
	xappend "--interface=$ifname"
}

append_listenaddress() {
	xappend "--listen-address=$1"
}

append_notinterface() {
	network_get_device ifname "$1" || ifname="$1"
	xappend "--except-interface=$ifname"
}

append_addnhosts() {
	xappend "--addn-hosts=$1"
}

append_bogusnxdomain() {
	xappend "--bogus-nxdomain=$1"
}

append_pxe_service() {
	xappend "--pxe-service=$1"
}

append_interface_name() {
	xappend "--interface-name=$1,$2"
}

filter_dnsmasq() {
	local cfg="$1" func="$2" match_cfg="$3" found_cfg

	# use entry when no instance entry set, or if it matches
	config_get found_cfg "$cfg" "instance"
	if [ -z "$found_cfg" -o "$found_cfg" = "$match_cfg" ]; then
		$func $cfg
	fi
}

dhcp_subscrid_add() {
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get subscriberid "$cfg" subscriberid
	[ -n "$subscriberid" ] || return 0

	xappend "--dhcp-subscrid=$networkid,$subscriberid"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_remoteid_add() {
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get remoteid "$cfg" remoteid
	[ -n "$remoteid" ] || return 0

	xappend "--dhcp-remoteid=$networkid,$remoteid"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_circuitid_add() {
	# TODO: DHCPV6 does not have circuitid; catch "option6:"
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get circuitid "$cfg" circuitid
	[ -n "$circuitid" ] || return 0

	xappend "--dhcp-circuitid=$networkid,$circuitid"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_userclass_add() {
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get userclass "$cfg" userclass
	[ -n "$userclass" ] || return 0

	xappend "--dhcp-userclass=$networkid,$userclass"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_vendorclass_add() {
	# TODO: DHCPV6 vendor class has stricter definitions; catch? fixup?
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get vendorclass "$cfg" vendorclass
	[ -n "$vendorclass" ] || return 0

	xappend "--dhcp-vendorclass=$networkid,$vendorclass"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_match_add() {
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get match "$cfg" match
	[ -n "$match" ] || return 0

	xappend "--dhcp-match=$networkid,$match"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}

dhcp_host_add() {
	local cfg="$1"
	local hosttag nametime addrs duids macs tags

	config_get_bool force "$cfg" force 0

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] && dhcp_option_add "$cfg" "$networkid" "$force"

	config_get_bool enable "$cfg" enable 1
	[ "$enable" = "0" ] && return 0

	config_get name "$cfg" name
	config_get ip "$cfg" ip
	config_get hostid "$cfg" hostid

	[ -n "$ip" -o -n "$name" -o -n "$hostid" ] || return 0

	config_get_bool dns "$cfg" dns 0
	[ "$dns" = "1" -a -n "$ip" -a -n "$name" ] && {
		echo "$ip $name${DOMAIN:+.$DOMAIN}" >> $HOSTFILE_TMP
	}

	config_get mac "$cfg" mac
	config_get duid "$cfg" duid
	config_get tag "$cfg" tag
	config_get gw "$cfg" gw

	if [ -n "$mac" ]; then
		# --dhcp-host=00:20:e0:3b:13:af,192.168.0.199,lap
		# many MAC are possible to track a laptop ON/OFF dock
		for m in $mac; do append macs "$m" ","; done
	fi

	if [ $DNSMASQ_DHCP_VER -eq 6 -a -n "$duid" ]; then
		# --dhcp-host=id:00:03:00:01:12:00:00:01:02:03,[::beef],lap
		# one (virtual) machine gets one DUID per RFC3315
		duids="id:${duid// */}"
	fi

	if [ -z "$macs" -a -z "$duids" ]; then
		# --dhcp-host=lap,192.168.0.199,[::beef]
		[ -n "$name" ] || return 0
		macs="$name"
		name=""
	fi

	if [ -n "$hostid" ]; then
		hex_to_hostid hostid "$hostid"
	fi

	if [ -n "$tag" ]; then
		for t in $tag; do append tags "$t" ",set:"; done
	fi

	if [ -n "$gw" ]; then
		append tags "$cfg" ",set:"
	fi

	config_get_bool broadcast "$cfg" broadcast 0
	config_get leasetime "$cfg" leasetime

	[ "$broadcast" = "0" ] && broadcast= || broadcast=",set:needs-broadcast"

	hosttag="${networkid:+,set:${networkid}}${tags:+,set:${tags}}$broadcast"
	nametime="${name:+,$name}${leasetime:+,$leasetime}"

	if [ $DNSMASQ_DHCP_VER -eq 6 ]; then
		addrs="${ip:+,$ip}${hostid:+,[::$hostid]}"
		xappend "--dhcp-host=$macs${duids:+,$duids}$hosttag$addrs$nametime"
	else
		xappend "--dhcp-host=$macs$hosttag${ip:+,$ip}$nametime"
	fi
	if [ -n "$gw" ]; then
		xappend "--dhcp-option=tag:$cfg,option:router,$gw"
	fi
}

dhcp_this_host_add() {
	local net="$1"
	local ifname="$2"
	local mode="$3"
	local routerstub routername ifdashname
	local lanaddr lanaddr6 lanaddrs6 ulaprefix

	if [ "$mode" -gt 0 ] ; then
		ifdashname="${ifname//./-}"
		routerstub="$( md5sum /etc/os-release )"
		routerstub="router-${routerstub// */}"
		routername="$( uci_get system @system[0] hostname $routerstub )"

		if [ "$mode" -gt 1 ] ; then
			if [ "$mode" -gt 2 ] ; then
				if [ "$mode" -gt 3 ] ; then
					append_interface_name "$ifdashname.$routername.$DOMAIN" "$ifname"
				fi

				append_interface_name "$routername.$DOMAIN" "$ifname"
			fi

			# All IP addresses discovered by dnsmasq will be labeled (except fe80::)
			append_interface_name "$routername" "$ifname"

		else
			# This uses a static host file entry for only limited addresses.
			# Use dnsmasq option "--expandhosts" to enable FQDN on host files.
			ulaprefix="$(uci_get network @globals[0] ula_prefix)"
			network_get_ipaddr lanaddr "$net"
			network_get_ipaddrs6 lanaddrs6 "$net"

			if [ -n "$lanaddr" ] ; then
				dhcp_domain_add "" "$routername" "$lanaddr"
			fi

			if [ -n "$ulaprefix" -a -n "$lanaddrs6" ] ; then
				for lanaddr6 in $lanaddrs6 ; do
					case "$lanaddr6" in
					"${ulaprefix%%:/*}"*)
						dhcp_domain_add "" "$routername" "$lanaddr6"
						;;
					esac
				done
			fi
		fi
	fi
}

dhcp_tag_add() {
	# NOTE: dnsmasq has explicit "option6:" prefix for DHCPv6 so no collisions
	local cfg="$1"

	tag="$cfg"

	[ -n "$tag" ] || return 0

	config_get_bool force "$cfg" force 0
	[ "$force" = "0" ] && force=

	config_get option "$cfg" dhcp_option
	for o in $option; do
		xappend "--dhcp-option${force:+-force}=tag:$tag,$o"
	done
}

dhcp_mac_add() {
	local cfg="$1"

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || return 0

	config_get mac "$cfg" mac
	[ -n "$mac" ] || return 0

	xappend "--dhcp-mac=$networkid,$mac"

	dhcp_option_add "$cfg" "$networkid"
}

dhcp_boot_add() {
	# TODO: BOOTURL is different between DHCPv4 and DHCPv6
	local cfg="$1"

	config_get networkid "$cfg" networkid

	config_get filename "$cfg" filename
	[ -n "$filename" ] || return 0

	config_get servername "$cfg" servername
	config_get serveraddress "$cfg" serveraddress

	[ -n "$serveraddress" -a ! -n "$servername" ] && return 0

	xappend "--dhcp-boot=${networkid:+net:$networkid,}${filename}${servername:+,$servername}${serveraddress:+,$serveraddress}"

	config_get_bool force "$cfg" force 0

	dhcp_option_add "$cfg" "$networkid" "$force"
}


dhcp_add() {
	local cfg="$1"
	local dhcp6range="::"
	local nettag
	local tags

	config_get net "$cfg" interface
	[ -n "$net" ] || return 0

	config_get networkid "$cfg" networkid
	[ -n "$networkid" ] || networkid="$net"

	network_get_device ifname "$net" || return 0

	[ "$cachelocal" = "0" ] && network_get_dnsserver dnsserver "$net" && {
		DNS_SERVERS="$DNS_SERVERS $dnsserver"
	}

	append_bool "$cfg" ignore "--no-dhcp-interface=$ifname" && {
		# Many ISP do not have useful names for DHCP customers (your WAN).
		dhcp_this_host_add "$net" "$ifname" "$ADD_WAN_FQDN"
		return 0
	}

	network_get_subnet subnet "$net" || return 0
	network_get_protocol proto "$net" || return 0

	# Do not support non-static interfaces for now
	[ static = "$proto" ] || return 0

	# Override interface netmask with dhcp config if applicable
	config_get netmask "$cfg" netmask "${subnet##*/}"

	#check for an already active dhcp server on the interface, unless 'force' is set
	config_get_bool force "$cfg" force 0
	[ $force -gt 0 ] || dhcp_check "$ifname" || return 0

	config_get start "$cfg" start 100
	config_get limit "$cfg" limit 150
	config_get leasetime "$cfg" leasetime 12h
	config_get options "$cfg" options
	config_get_bool dynamicdhcp "$cfg" dynamicdhcp 1

	config_get dhcpv4 "$cfg" dhcpv4
	config_get dhcpv6 "$cfg" dhcpv6

	config_get ra "$cfg" ra
	config_get ra_management "$cfg" ra_management
	config_get ra_preference "$cfg" ra_preference
	config_get dns "$cfg" dns

	config_list_foreach "$cfg" "interface_name" append_interface_name "$ifname"

	# Put the router host name on this DHCP served interface address(es)
	dhcp_this_host_add "$net" "$ifname" "$ADD_LOCAL_FQDN"

	start="$( dhcp_calc "$start" )"

	add_tag() {
		tags="${tags}tag:$1,"
	}
	config_list_foreach "$cfg" tag add_tag

	nettag="${networkid:+set:${networkid},}"

	if [ "$limit" -gt 0 ] ; then
		limit=$((limit-1))
	fi

	eval "$(ipcalc.sh "${subnet%%/*}" $netmask $start $limit)"

	if [ "$dynamicdhcp" = "0" ] ; then
		END="static"
		dhcp6range="::,static"
	else
		dhcp6range="::1000,::ffff"
	fi


	if [ "$dhcpv4" != "disabled" ] ; then
		xappend "--dhcp-range=$tags$nettag$START,$END,$NETMASK,$leasetime${options:+ $options}"
	fi


	if [ $DNSMASQ_DHCP_VER -eq 6 -a "$ra" = "server" ] ; then
		# Note: dnsmasq cannot just be a DHCPv6 server (all-in-1)
		# and let some other machine(s) send RA pointing to it.

		case $ra_preference in
		*high*)
			xappend "--ra-param=$ifname,high,0,7200"
			;;
		*low*)
			xappend "--ra-param=$ifname,low,0,7200"
			;;
		*)
			# Send UNSOLICITED RA at default interval and live for 2 hours.
			# TODO: convert flexible lease time into route life time (only seconds).
			xappend "--ra-param=$ifname,0,7200"
			;;
		esac

		if [ "$dhcpv6" = "disabled" ] ; then
			ra_management="3"
		fi


		case $ra_management in
		0)
			# SLACC with DCHP for extended options
			xappend "--dhcp-range=$nettag::,constructor:$ifname,ra-stateless,ra-names"
			;;
		2)
			# DHCP address and RA only for management redirection
			xappend "--dhcp-range=$nettag$dhcp6range,constructor:$ifname,$leasetime"
			;;
		3)
			# SLAAC only but dnsmasq attempts to link HOSTNAME, DHCPv4 MAC, and SLAAC
			xappend "--dhcp-range=$nettag::,constructor:$ifname,ra-only,ra-names"
			;;
		*)
			# SLAAC and full DHCP
			xappend "--dhcp-range=$nettag$dhcp6range,constructor:$ifname,slaac,ra-names,$leasetime"
			;;
		esac

		if [ -n "$dns" ]; then
			dnss=""
			for d in $dns; do append dnss "[$d]" ","; done
		else
			dnss="[::]"
		fi

		dhcp_option_append "option6:dns-server,$dnss" "$networkid"
	fi

	dhcp_option_add "$cfg" "$networkid" 0
	dhcp_option_add "$cfg" "$networkid" 2
}

dhcp_option_append() {
	local option="$1"
	local networkid="$2"
	local force="$3"

	xappend "--dhcp-option${force:+-force}=${networkid:+$networkid,}$option"
}

dhcp_option_add() {
	# NOTE: dnsmasq has explicit "option6:" prefix for DHCPv6 so no collisions
	local cfg="$1"
	local networkid="$2"
	local force="$3"
	local opt="dhcp_option"

	[ "$force" = "0" ] && force=
	[ "$force" = "2" ] && opt="dhcp_option_force"

	local list_len
	config_get list_len "$cfg" "${opt}_LENGTH"

	if [ -n "$list_len" ]; then
		config_list_foreach "$cfg" "$opt" dhcp_option_append "$networkid" "$force"
	else
		config_get dhcp_option "$cfg" "$opt"

		[ -n "$dhcp_option" ] && echo "Warning: the 'option $opt' syntax is deprecated, use 'list $opt'" >&2

		local option
		for option in $dhcp_option; do
			dhcp_option_append "$option" "$networkid" "$force"
		done
	fi
}

dhcp_domain_add() {
	local cfg="$1"
	local ip name names record

	config_get names "$cfg" name "$2"
	[ -n "$names" ] || return 0

	config_get ip "$cfg" ip "$3"
	[ -n "$ip" ] || return 0

	for name in $names; do
		record="${record:+$record }$name"
	done

	echo "$ip $record" >> $HOSTFILE_TMP
}

dhcp_srv_add() {
	local cfg="$1"

	config_get srv "$cfg" srv
	[ -n "$srv" ] || return 0

	config_get target "$cfg" target
	[ -n "$target" ] || return 0

	config_get port "$cfg" port
	[ -n "$port" ] || return 0

	config_get class "$cfg" class
	config_get weight "$cfg" weight

	local service="$srv,$target,$port${class:+,$class${weight:+,$weight}}"

	xappend "--srv-host=$service"
}

dhcp_mx_add() {
	local cfg="$1"
	local domain relay pref

	config_get domain "$cfg" domain
	[ -n "$domain" ] || return 0

	config_get relay "$cfg" relay
	[ -n "$relay" ] || return 0

	config_get pref "$cfg" pref 0

	local service="$domain,$relay,$pref"

	xappend "--mx-host=$service"
}

dhcp_cname_add() {
	local cfg="$1"
	local cname target

	config_get cname "$cfg" cname
	[ -n "$cname" ] || return 0

	config_get target "$cfg" target
	[ -n "$target" ] || return 0

	xappend "--cname=${cname},${target}"
}

dhcp_hostrecord_add() {
	local cfg="$1"
	local names addresses record val

	config_get names "$cfg" name "$2"
	if [ -z "$names" ]; then
		return 0
	fi

	config_get addresses "$cfg" ip "$3"
	if [ -z "$addresses" ]; then
		return 0
	fi

	for val in $names $addresses; do
		record="${record:+$record,}$val"
	done

	xappend "--host-record=$record"
}

dhcp_relay_add() {
	local cfg="$1"
	local local_addr server_addr interface

	config_get local_addr "$cfg" local_addr
	[ -n "$local_addr" ] || return 0

	config_get server_addr "$cfg" server_addr
	[ -n "$server_addr" ] || return 0

	config_get interface "$cfg" interface
	if [ -z "$interface" ]; then
		xappend "--dhcp-relay=$local_addr,$server_addr"
	else
		network_get_device ifname "$interface" || return
		xappend "--dhcp-relay=$local_addr,$server_addr,$ifname"
	fi
}

dnsmasq_start()
{
	local cfg="$1" disabled resolvfile user_dhcpscript

	config_get_bool disabled "$cfg" disabled 0
	[ "$disabled" -gt 0 ] && return 0

	# reset list of DOMAINS and DNS servers (for each dnsmasq instance)
	DNS_SERVERS=""
	DOMAIN=""
	CONFIGFILE="${BASECONFIGFILE}.${cfg}"
	CONFIGFILE_TMP="${CONFIGFILE}.$$"
	HOSTFILE="${BASEHOSTFILE}.${cfg}"
	HOSTFILE_TMP="${HOSTFILE}.$$"
	BASEDHCPSTAMPFILE_CFG="${BASEDHCPSTAMPFILE}.${cfg}"

	# before we can call xappend
	mkdir -p /var/run/dnsmasq/
	mkdir -p $(dirname $CONFIGFILE)
	mkdir -p $(dirname $HOSTFILE)
	mkdir -p /var/lib/misc
	chown dnsmasq:dnsmasq /var/run/dnsmasq

	echo "# auto-generated config file from /etc/config/dhcp" > $CONFIGFILE_TMP
	echo "# auto-generated config file from /etc/config/dhcp" > $HOSTFILE_TMP

	local dnsmasqconffile="/etc/dnsmasq.${cfg}.conf"
	if [ ! -r "$dnsmasqconffile" ]; then
		dnsmasqconffile=/etc/dnsmasq.conf
	fi

	# if we did this last, we could override auto-generated config
	[ -f "${dnsmasqconffile}" ] && {
		xappend "--conf-file=${dnsmasqconffile}"
	}

	$PROG --version | grep -osqE "^Compile time options:.* DHCPv6( |$)" && DHCPv6CAPABLE=1 || DHCPv6CAPABLE=0


	if [ -x /usr/sbin/odhcpd -a -x /etc/init.d/odhcpd ] ; then
		local odhcpd_is_main odhcpd_is_enabled
		config_get odhcpd_is_main odhcpd maindhcp 0
		/etc/init.d/odhcpd enabled && odhcpd_is_enabled=1 || odhcpd_is_enabled=0


		if [ "$odhcpd_is_enabled" -eq 0 -a "$DHCPv6CAPABLE" -eq 1 ] ; then
			# DHCP V4 and V6 in DNSMASQ
			DNSMASQ_DHCP_VER=6
		elif [ "$odhcpd_is_main" -gt 0 ] ; then
			# ODHCPD is doing it all
			DNSMASQ_DHCP_VER=0
		else
			# You have ODHCPD but use DNSMASQ for DHCPV4
			DNSMASQ_DHCP_VER=4
		fi

	elif [ "$DHCPv6CAPABLE" -eq 1 ] ; then
		# DHCP V4 and V6 in DNSMASQ
		DNSMASQ_DHCP_VER=6
	else
		DNSMASQ_DHCP_VER=4
	fi

	# Allow DHCP/DHCPv6 to be handled by ISC DHCPD
	if [ -x /usr/sbin/dhcpd ] ; then
		if [ -x /etc/init.d/dhcpd ] ; then
			/etc/init.d/dhcpd enabled && DNSMASQ_DHCP_VER=0
		fi
		if [ -x /etc/init.d/dhcpd6 -a "$DNSMASQ_DHCP_VER" -gt 0 ] ; then
			/etc/init.d/dhcpd6 enabled && DNSMASQ_DHCP_VER=4
		fi
	fi

	append_bool "$cfg" authoritative "--dhcp-authoritative"
	append_bool "$cfg" nodaemon "--no-daemon"
	append_bool "$cfg" domainneeded "--domain-needed"
	append_bool "$cfg" filterwin2k "--filterwin2k"
	append_bool "$cfg" nohosts "--no-hosts"
	append_bool "$cfg" nonegcache "--no-negcache"
	append_bool "$cfg" strictorder "--strict-order"
	append_bool "$cfg" logqueries "--log-queries=extra"
	append_bool "$cfg" noresolv "--no-resolv"
	append_bool "$cfg" localise_queries "--localise-queries"
	append_bool "$cfg" readethers "--read-ethers"
	append_bool "$cfg" dbus "--enable-dbus"
	append_bool "$cfg" expandhosts "--expand-hosts"
	config_get tftp_root "$cfg" "tftp_root"
	[ -n "$tftp_root" ] && mkdir -p "$tftp_root" && append_bool "$cfg" enable_tftp "--enable-tftp"
	append_bool "$cfg" tftp_no_fail "--tftp-no-fail"
	append_bool "$cfg" nonwildcard "--bind-dynamic" 1
	append_bool "$cfg" fqdn "--dhcp-fqdn"
	append_bool "$cfg" proxydnssec "--proxy-dnssec"
	append_bool "$cfg" localservice "--local-service"
	append_bool "$cfg" logdhcp "--log-dhcp"
	append_bool "$cfg" quietdhcp "--quiet-dhcp"
	append_bool "$cfg" sequential_ip "--dhcp-sequential-ip"
	append_bool "$cfg" allservers "--all-servers"
	append_bool "$cfg" noping "--no-ping"

	append_parm "$cfg" logfacility "--log-facility"

	append_parm "$cfg" cachesize "--cache-size"
	append_parm "$cfg" dnsforwardmax "--dns-forward-max"
	append_parm "$cfg" port "--port"
	append_parm "$cfg" ednspacket_max "--edns-packet-max"
	append_parm "$cfg" dhcpleasemax "--dhcp-lease-max"
	append_parm "$cfg" "queryport" "--query-port"
	append_parm "$cfg" "minport" "--min-port"
	append_parm "$cfg" "maxport" "--max-port"
	append_parm "$cfg" "domain" "--domain"
	append_parm "$cfg" "local" "--server"
	config_list_foreach "$cfg" "listen_address" append_listenaddress
	config_list_foreach "$cfg" "server" append_server
	config_list_foreach "$cfg" "rev_server" append_rev_server
	config_list_foreach "$cfg" "address" append_address
	config_list_foreach "$cfg" "ipset" append_ipset
	[ -n "$BOOT" ] || {
		config_list_foreach "$cfg" "interface" append_interface
		config_list_foreach "$cfg" "notinterface" append_notinterface
	}
	config_list_foreach "$cfg" "addnhosts" append_addnhosts
	config_list_foreach "$cfg" "bogusnxdomain" append_bogusnxdomain
	append_parm "$cfg" "leasefile" "--dhcp-leasefile" "/tmp/dhcp.leases"
	append_parm "$cfg" "serversfile" "--servers-file"
	append_parm "$cfg" "tftp_root" "--tftp-root"
	append_parm "$cfg" "dhcp_boot" "--dhcp-boot"
	append_parm "$cfg" "local_ttl" "--local-ttl"
	append_parm "$cfg" "pxe_prompt" "--pxe-prompt"
	config_list_foreach "$cfg" "pxe_service" append_pxe_service
	config_get DOMAIN "$cfg" domain

	config_get_bool ADD_LOCAL_DOMAIN "$cfg" add_local_domain 1
	config_get_bool ADD_LOCAL_HOSTNAME "$cfg" add_local_hostname 1
	config_get ADD_LOCAL_FQDN "$cfg" add_local_fqdn ""
	config_get ADD_WAN_FQDN "$cfg" add_wan_fqdn 0

	if [ -z "$ADD_LOCAL_FQDN" ] ; then
		# maintain support for previous UCI
		ADD_LOCAL_FQDN="$ADD_LOCAL_HOSTNAME"
	fi

	config_get_bool readethers "$cfg" readethers
	[ "$readethers" = "1" -a \! -e "/etc/ethers" ] && touch /etc/ethers

	config_get user_dhcpscript $cfg dhcpscript
	if has_handler || [ -n "$user_dhcpscript" ]; then
		xappend "--dhcp-script=$DHCPSCRIPT"
	fi

	config_get leasefile $cfg leasefile "/tmp/dhcp.leases"
	[ -n "$leasefile" -a \! -e "$leasefile" ] && touch "$leasefile"
	config_get_bool cachelocal "$cfg" cachelocal 1

	config_get_bool noresolv "$cfg" noresolv 0
	if [ "$noresolv" != "1" ]; then
		config_get resolvfile "$cfg" resolvfile "/tmp/resolv.conf.auto"
		# So jail doesn't complain if file missing
		[ -n "$resolvfile" -a \! -e "$resolvfile" ] && touch "$resolvfile"
	fi

	[ -n "$resolvfile" ] && xappend "--resolv-file=$resolvfile"

	config_get hostsfile "$cfg" dhcphostsfile
	[ -e "$hostsfile" ] && xappend "--dhcp-hostsfile=$hostsfile"

	local rebind
	config_get_bool rebind "$cfg" rebind_protection 1
	[ $rebind -gt 0 ] && {
		log_once \
			"DNS rebinding protection is active," \
			"will discard upstream RFC1918 responses!"
		xappend "--stop-dns-rebind"

		local rebind_localhost
		config_get_bool rebind_localhost "$cfg" rebind_localhost 0
		[ $rebind_localhost -gt 0 ] && {
			log_once "Allowing 127.0.0.0/8 responses"
			xappend "--rebind-localhost-ok"
		}

		append_rebind_domain() {
			log_once "Allowing RFC1918 responses for domain $1"
			xappend "--rebind-domain-ok=$1"
		}

		config_list_foreach "$cfg" rebind_domain append_rebind_domain
	}

	config_get_bool dnssec "$cfg" dnssec 0
	[ "$dnssec" -gt 0 ] && {
		xappend "--conf-file=$TRUSTANCHORSFILE"
		xappend "--dnssec"
		[ -x /etc/init.d/sysntpd ] && {
			/etc/init.d/sysntpd enabled
			[ "$?" -ne 0 -o "$(uci_get system.ntp.enabled)" = "1" ] && {
				[ -f "$TIMEVALIDFILE" ] || xappend "--dnssec-no-timecheck"
			}
		}
		append_bool "$cfg" dnsseccheckunsigned "--dnssec-check-unsigned"
	}

	config_get addmac "$cfg" addmac 0
	[ "$addmac" != "0" ] && {
		[ "$addmac" = "1" ] && addmac=
		xappend "--add-mac${addmac:+="$addmac"}"
	}

	dhcp_option_add "$cfg" "" 0
	dhcp_option_add "$cfg" "" 2

	xappend "--dhcp-broadcast=tag:needs-broadcast"

	xappend "--addn-hosts=$(dirname $HOSTFILE)"

	config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq.d"
	xappend "--conf-dir=$dnsmasqconfdir"
	dnsmasqconfdir="${dnsmasqconfdir%%,*}"
	[ ! -d "$dnsmasqconfdir" ] && mkdir -p $dnsmasqconfdir
	xappend "--user=dnsmasq"
	xappend "--group=dnsmasq"
	echo >> $CONFIGFILE_TMP

	config_get_bool enable_tftp "$cfg" enable_tftp 0
	[ "$enable_tftp" -gt 0 ] && {
		config_get tftp_root "$cfg" tftp_root
		append EXTRA_MOUNT $tftp_root
	}

	config_foreach filter_dnsmasq host dhcp_host_add "$cfg"
	echo >> $CONFIGFILE_TMP
	config_foreach filter_dnsmasq boot dhcp_boot_add "$cfg"
	config_foreach filter_dnsmasq mac dhcp_mac_add "$cfg"
	config_foreach filter_dnsmasq tag dhcp_tag_add "$cfg"
	config_foreach filter_dnsmasq vendorclass dhcp_vendorclass_add "$cfg"
	config_foreach filter_dnsmasq userclass dhcp_userclass_add "$cfg"
	config_foreach filter_dnsmasq circuitid dhcp_circuitid_add "$cfg"
	config_foreach filter_dnsmasq remoteid dhcp_remoteid_add "$cfg"
	config_foreach filter_dnsmasq subscrid dhcp_subscrid_add "$cfg"
	config_foreach filter_dnsmasq match dhcp_match_add "$cfg"
	config_foreach filter_dnsmasq domain dhcp_domain_add "$cfg"
	config_foreach filter_dnsmasq hostrecord dhcp_hostrecord_add "$cfg"
	[ -n "$BOOT" ] || config_foreach filter_dnsmasq relay dhcp_relay_add "$cfg"

	echo >> $CONFIGFILE_TMP
	config_foreach filter_dnsmasq srvhost dhcp_srv_add "$cfg"
	config_foreach filter_dnsmasq mxhost dhcp_mx_add "$cfg"
	echo >> $CONFIGFILE_TMP

	config_get_bool boguspriv "$cfg" boguspriv 1
	[ "$boguspriv" -gt 0 ] && {
		xappend "--bogus-priv"
		[ -r "$RFC6761FILE" ] && xappend "--conf-file=$RFC6761FILE"
	}

	if [ "$DNSMASQ_DHCP_VER" -gt 4 ] ; then
		# Enable RA feature for when/if it is constructed,
		# and RA is selected per interface pool (RA, DHCP, or both),
		# but no one (should) want RA broadcast in syslog
		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg"
		xappend "--enable-ra"
		xappend "--quiet-ra"
		append_bool "$cfg" quietdhcp "--quiet-dhcp6"

	elif [ "$DNSMASQ_DHCP_VER" -gt 0 ] ; then
		[ -n "$BOOT" ] || config_foreach filter_dnsmasq dhcp dhcp_add "$cfg"
	fi


	echo >> $CONFIGFILE_TMP
	config_foreach filter_dnsmasq cname dhcp_cname_add "$cfg"
	echo >> $CONFIGFILE_TMP

	echo >> $CONFIGFILE_TMP
	mv -f $CONFIGFILE_TMP $CONFIGFILE
	mv -f $HOSTFILE_TMP $HOSTFILE

	[ "$resolvfile" = "/tmp/resolv.conf.auto" ] && {
		rm -f /tmp/resolv.conf
		[ $ADD_LOCAL_DOMAIN -eq 1 ] && [ -n "$DOMAIN" ] && {
			echo "search $DOMAIN" >> /tmp/resolv.conf
		}
		DNS_SERVERS="$DNS_SERVERS 127.0.0.1"
		for DNS_SERVER in $DNS_SERVERS ; do
			echo "nameserver $DNS_SERVER" >> /tmp/resolv.conf
		done
	}

	procd_open_instance $cfg
	procd_set_param command $PROG -C $CONFIGFILE -k -x /var/run/dnsmasq/dnsmasq."${cfg}".pid
	procd_set_param file $CONFIGFILE
	[ -n "$user_dhcpscript" ] && procd_set_param env USER_DHCPSCRIPT="$user_dhcpscript"
	procd_set_param respawn

	procd_add_jail dnsmasq ubus log
	procd_add_jail_mount $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE $RFC6761FILE /etc/passwd /etc/group /etc/TZ /dev/null /dev/urandom $dnsmasqconffile $dnsmasqconfdir $resolvfile $user_dhcpscript /etc/hosts /etc/ethers /sbin/hotplug-call $EXTRA_MOUNT $DHCPSCRIPT
	procd_add_jail_mount_rw /var/run/dnsmasq/ $leasefile

	procd_close_instance
}

dnsmasq_stop()
{
	local cfg="$1" resolvfile

	config_get resolvfile "$cfg" "resolvfile"

	#relink /tmp/resolve.conf only for main instance
	[ "$resolvfile" = "/tmp/resolv.conf.auto" ] && {
		[ -f /tmp/resolv.conf ] && {
			rm -f /tmp/resolv.conf
			ln -s "$resolvfile" /tmp/resolv.conf
		}
	}

	rm -f ${BASEDHCPSTAMPFILE}.${cfg}.*.dhcp
}

add_interface_trigger()
{
	local interface ignore

	config_get interface "$1" interface
	config_get_bool ignore "$1" ignore 0

	[ -n "$interface" -a $ignore -eq 0 ] && procd_add_interface_trigger "interface.*" "$interface" /etc/init.d/dnsmasq reload
}

service_triggers()
{
	procd_add_reload_trigger "dhcp" "system"

	config_load dhcp
	config_foreach add_interface_trigger dhcp
	config_foreach add_interface_trigger relay
}

boot()
{
	BOOT=1
	start "$@"
}

start_service() {
	local instance="$1"
	local instance_found=0

	. /lib/functions/network.sh

	config_cb() {
		local type="$1"
		local name="$2"
		if [ "$type" = "dnsmasq" ]; then
			if [ -n "$instance" -a "$instance" = "$name" ]; then
				instance_found=1
			fi
		fi
	}

	config_load dhcp

	if [ -n "$instance" ]; then
		[ "$instance_found" -gt 0 ] || return
		dnsmasq_start "$instance"
	else
		config_foreach dnsmasq_start dnsmasq
	fi
}

reload_service() {
	rc_procd start_service "$@"
	procd_send_signal dnsmasq "$@"
}

stop_service() {
	local instance="$1"
	local instance_found=0

	config_cb() {
		local type="$1"
		local name="$2"
		if [ "$type" = "dnsmasq" ]; then
			if [ -n "$instance" -a "$instance" = "$name" ]; then
				instance_found=1
			fi
		fi
	}

	config_load dhcp

	if [ -n "$instance" ]; then
		[ "$instance_found" -gt 0 ] || return
		dnsmasq_stop "$instance"
	else
		config_foreach dnsmasq_stop dnsmasq
	fi
}
root@OpenMPTCProuter:~#

(/off

well, i can source a file and not use so in my view it is just an include not a real execution. anyhow that's least interesting for you.)

ok, i think my guess got confirmed. you cannot use jhsn.sh because procd_jail is in use but the jhsn.sh is missing from the allowed jailed files.

(note: I broke lines to more readable length)

DHCPSCRIPT="/usr/lib/dnsmasq/dhcp-script.sh"
[..]
procd_add_jail dnsmasq ubus log
	procd_add_jail_mount $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE \
 $RFC6761FILE /etc/passwd /etc/group /etc/TZ /dev/null \
/dev/urandom $dnsmasqconffile $dnsmasqconfdir $resolvfile \
$user_dhcpscript /etc/hosts /etc/ethers /sbin/hotplug-call $EXTRA_MOUNT $DHCPSCRIPT
	procd_add_jail_mount_rw /var/run/dnsmasq/ $leasefile

the version i use (OpenWrt 22.03.2, r19803-9a599fee93) has a different dnsmasq service file:

DHCPSCRIPT="/usr/lib/dnsmasq/dhcp-script.sh"
DHCPSCRIPT_DEPENDS="/usr/share/libubox/jshn.sh /usr/bin/jshn /bin/ubus"
[..]
        procd_add_jail dnsmasq ubus log
        procd_add_jail_mount $CONFIGFILE $DHCPBOGUSHOSTNAMEFILE $DHCPSCRIPT $DHCPSCRIPT_DEPENDS
        procd_add_jail_mount $EXTRA_MOUNT $RFC6761FILE $TRUSTANCHORSFILE
        procd_add_jail_mount $dnsmasqconffile $dnsmasqconfdir $resolvdir $user_dhcpscript $hostsfile
        procd_add_jail_mount /etc/passwd /etc/group /etc/TZ /etc/hosts /etc/ethers
        procd_add_jail_mount_rw /var/run/dnsmasq/ $leasefile

you can see files listed as $DHCPSCRIPT_DEPENDS are added to jail, which includes jhsn.sh so it is accessible from the jailed process.

why you have your version of dnsmasq service i don't know. do you use latest official owrt version or some customized / forked non-official version maybe?

2 Likes

Thank you for the investigation. The reason is clear now. I'm using openmptcprouter build. It is customized version of openwrt but I'm pretty sure dnsmasq sholdn't be customized. The question is how to install right version of dnsmasq over installed old one.

BTW, I'm using dnsmasq-full package. Are you using dnsmasq or dnsmasq-full? Do these 2 have the same service file?

not sure if full would have a different service file, i guess minimal to no changes should be, but how to jail the process logic should be the same.

you should take up this with openmptcprouter imho, and meanwhile simply copy the official version solution and it will work for you too.

Could you, please, post here the service file you have in your system

i tried but there is a char limit here. you may check it in git: https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=package/network/services/dnsmasq/files/dnsmasq.init;h=c4ca3eb2db39d08355ec82904456a54fa8648003;hb=refs/heads/openwrt-22.03

Ok, thanks. I installed it on top of the old one and checked. So far so good. Everything is working fine now. Thanks again for your help

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