Used by dhcpcd to run hook scripts
found in
/lib/dhcpcd/dhcpcd-hooks
and /etc/dhcpcd.enter-hook and
/etc/dhcpcd.exit-hook. (may be empty)
The default scripts configure /etc/resolv.conf and the hostname.
May include scripts to configure ntp or ypbind.
A test hook echos the dhcp variables from DISCOVER message.
Set $interface and $reason before calling dhcpcd-run-hooks
DHCP information to be configured is in variables starting with new_ and old DHCP
information (to be removed) is in variables starting with old_.
dhcpcd displays the list of variables using -V, --variables
Reasons dhcpcd-run-hooks invoked:
PREINIT |
dhcpcd will clear the environment variables aside from $PATH and $RC_SVCNAME. $interface_order |
/etc/dhcpcd.enter-hook, /lib/dhcpcd/dhcpcd-hooks in a lexical order and then finally /etc/dhcpcd.exit-hook
/lib/dhcpcd/dhcpcd-hooks > wc *
8 37 283 01-test
8 23 120 02-dump
120 367 2930 10-wpa_supplicant
204 718 5588 20-resolv.conf
155 454 3503 30-hostname
141 491 3472 50-ntp.conf
01-test
if [ "$reason" = "TEST" ]; then
set | grep "^\(interface\|pid\|reason\|profile\|skip_hooks\)=" | sort
set | grep "^if\(carrier\|flags\|mtu\|wireless\|ssid\)=" | sort
set | grep "^\(new_\|old_\|nd[0-9]*_\)" | sort
exit 0
fi
02-dump
# Just echo our DHCP options we have
case "$reason" in
DUMP|DUMP6)
set | sed -ne 's/^new_//p' | sort
exit 0
;;
esac
10-wpa_supplicant (edited ed)
# Start, reconfigure and stop wpa_supplicant per wireless interface.
# needed because wpa_supplicant lacks hotplugging
if [ -z "$wpa_supplicant_conf" ]; then
for x in \
/etc/wpa_supplicant/wpa_supplicant-"$interface".conf \ # ex: /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
/etc/wpa_supplicant/wpa_supplicant.conf \
/etc/wpa_supplicant-"$interface".conf \ # ex: /etc/wpa_supplicant-wlan0.conf
/etc/wpa_supplicant.conf \
; do
if [ -s "$x" ]; then wpa_supplicant_conf="$x" break fi
done
fi
: ${wpa_supplicant_conf:=/etc/wpa_supplicant.conf}
wpa_supplicant_ctrldir()
{ local dir
dir=$(key_get_value "[[:space:]]*ctrl_interface=" \ "$wpa_supplicant_conf")
dir=$(trim "$dir")
case "$dir" in
DIR=*)
dir=${dir##DIR=}
dir=${dir%%[[:space:]]GROUP=*}
dir=$(trim "$dir")
;;
esac
printf %s "$dir"
}
wpa_supplicant_start()
{ local dir err errn
[ "$ifcarrier" = "up" ] && return 0 # If the carrier is up, don't bother checking anything
# Pre flight checks
if [ ! -s "$wpa_supplicant_conf" ]; then syslog warn \ "$wpa_supplicant_conf does not exist"
syslog warn "not interacting with wpa_supplicant(8)" return 1 fi
dir=$(wpa_supplicant_ctrldir)
if [ -z "$dir" ]; then syslog warn \ "ctrl_interface not defined in $wpa_supplicant_conf"
syslog warn "not interacting with wpa_supplicant(8)" return 1 fi
wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 && return 0
syslog info "starting wpa_supplicant"
wpa_supplicant_driver="${wpa_supplicant_driver:-nl80211,wext}"
driver=${wpa_supplicant_driver:+-D}$wpa_supplicant_driver
err=$(wpa_supplicant -B -c"$wpa_supplicant_conf" -i"$interface" "$driver" 2>&1)
errn=$?
if [ $errn != 0 ]; then syslog err "failed to start wpa_supplicant" syslog err "$err" fi
return $errn
}
wpa_supplicant_reconfigure()
{ local dir err errn
dir=$(wpa_supplicant_ctrldir)
[ -z "$dir" ] && return 1
if ! wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1; then wpa_supplicant_start return $? fi
syslog info "reconfiguring wpa_supplicant"
err=$(wpa_cli -p "$dir" -i "$interface" reconfigure 2>&1)
errn=$?
if [ $errn != 0 ]; then syslog err "failed to reconfigure wpa_supplicant" syslog err "$err" fi
return $errn
}
wpa_supplicant_stop()
{ local dir err errn
dir=$(wpa_supplicant_ctrldir)
[ -z "$dir" ] && return 1
wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 || return 0
syslog info "stopping wpa_supplicant"
err=$(wpa_cli -i"$interface" terminate 2>&1)
errn=$?
if [ $errn != 0 ]; then syslog err "failed to start wpa_supplicant" syslog err "$err" fi
return $errn
}
if [ "$ifwireless" = "1" ] && \
type wpa_supplicant >/dev/null 2>&1 && \
type wpa_cli >/dev/null 2>&1
then
case "$reason" in
PREINIT) wpa_supplicant_start;;
RECONFIGURE) wpa_supplicant_reconfigure;;
DEPARTED) wpa_supplicant_stop;;
esac
fi
/lib/dhcpcd/dhcpcd-hooks >
cat 20-resolv.conf
# Generate /etc/resolv.conf
# Support resolvconf(8) if available
# merge dhcpcd resolv.conf files into one like resolvconf,
# but resolvconf is preferred as other applications like VPN clients can readily hook into it.
# Also, resolvconf can configure local nameservers such as bind or dnsmasq.
resolv_conf_dir="$state_dir/resolv.conf"
NL="
"
: ${resolvconf:=resolvconf}
build_resolv_conf()
{
local cf="$state_dir/resolv.conf.$ifname"
local interfaces= header= search= srvs= servers= x=
interfaces=$(list_interfaces "$resolv_conf_dir") # list of interfaces
if [ -n "$interfaces" ]; then # Build the resolv.conf
for x in ${interfaces}; do header="$header${header:+, }$x" done ## Build the header
domain=$(cd "$resolv_conf_dir"; \ key_get_value "domain " ${interfaces}) ## Build the search list
search=$(cd "$resolv_conf_dir"; \ key_get_value "search " ${interfaces})
set -- ${domain}
domain="$1"
[ -n "$2" ] && search="$search $*"
[ -n "$search" ] && search="$(uniqify $search)"
[ "$domain" = "$search" ] && search=
[ -n "$domain" ] && domain="domain $domain$NL"
[ -n "$search" ] && search="search $search$NL"
srvs=$(cd "$resolv_conf_dir"; \ key_get_value "nameserver " ${interfaces}) ## Build the nameserver list
for x in $(uniqify ${srvs}); do servers="${servers}nameserver $x$NL" done
fi
header="$signature_base${header:+ $from }$header"
# Assemble resolv.conf using our head and tail files
[ -f "$cf" ] && rm -f "$cf"
[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
echo "$header" > "$cf"
if [ -f /etc/resolv.conf.head ]; then cat /etc/resolv.conf.head >> "$cf"
else echo "# /etc/resolv.conf.head can replace this line" >> "$cf" fi
printf %s "$domain$search$servers" >> "$cf"
if [ -f /etc/resolv.conf.tail ]; then cat /etc/resolv.conf.tail >> "$cf"
else echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
fi
if change_file /etc/resolv.conf "$cf"; then chmod 644 /etc/resolv.conf fi
rm -f "$cf"
}
# Extract ND DNS options from the RA
# ignore the lifetime of the DNS options unless they are absent or zero.
# See draft-gont-6man-slaac-dns-config-issues-01 for issues regarding DNS option lifetime in ND messages.
eval_nd_dns()
{
eval ltime=\$nd${i}_rdnss${j}_lifetime
if [ -z "$ltime" -o "$ltime" = 0 ]; then rdnss=
else eval rdnss=\$nd${i}_rdnss${j}_servers
fi
eval ltime=\$nd${i}_dnssl${j}_lifetime
if [ -z "$ltime" -o "$ltime" = 0 ]; then dnssl=
else eval dnssl=\$nd${i}_dnssl${j}_search
fi
[ -z "$rdnss" -a -z "$dnssl" ] && return 1
[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
j=$(($j + 1))
return 0
}
add_resolv_conf()
{ local x= conf="$signature$NL" warn=true
local i j ltime rdnss dnssl new_rdnss new_dnssl
i=1
j=1
while true; do # Loop to extract the ND DNS options using our indexed shell values
while true; do eval_nd_dns || break done
i=$(($i + 1))
j=1
eval_nd_dns || break
done
[ -n "$new_rdnss" ] && new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
[ -n "$new_dnssl" ] && new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl"
# Derive a new domain from our various hostname options
if [ -z "$new_domain_name" ]; then
if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then new_domain_name="${new_dhcp6_fqdn#*.}"
elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then new_domain_name="${new_fqdn#*.}"
elif [ "$new_host_name" != "${new_host_name#*.}" ]; then new_domain_name="${new_host_name#*.}"
fi
fi
# If we don't have any configuration, remove it
if [ -z "$new_domain_name_servers" -a \
-z "$new_domain_name" -a \
-z "$new_domain_search" ]; then remove_resolv_conf return $?
fi
if [ -n "$new_domain_name" ]; then
set -- $new_domain_name
if valid_domainname "$1"; then conf="${conf}domain $1$NL"
else syslog err "Invalid domain name: $1"
fi
# If there is no search this, make this one
if [ -z "$new_domain_search" ]; then new_domain_search="$new_domain_name"
[ "$new_domain_name" = "$1" ] && warn=true
fi
fi
if [ -n "$new_domain_search" ]; then
if valid_domainname_list $new_domain_search; then conf="${conf}search $new_domain_search$NL"
elif ! $warn; then syslog err "Invalid domain name in list:" \ "$new_domain_search"
fi
fi
for x in ${new_domain_name_servers}; do conf="${conf}nameserver $x$NL" done
if type "$resolvconf" >/dev/null 2>&1; then
[ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
printf %s "$conf" | "$resolvconf" -a "$ifname"
return $?
fi
if [ -e "$resolv_conf_dir/$ifname" ]; then rm -f "$resolv_conf_dir/$ifname"
fi
[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
printf %s "$conf" > "$resolv_conf_dir/$ifname"
build_resolv_conf
}
remove_resolv_conf()
{ if type "$resolvconf" >/dev/null 2>&1; then "$resolvconf" -d "$ifname" -f
else
if [ -e "$resolv_conf_dir/$ifname" ]; then rm -f "$resolv_conf_dir/$ifname" fi
build_resolv_conf
fi
}
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
new_domain_name_servers="$new_dhcp6_name_servers"
new_domain_search="$new_dhcp6_domain_search"
;;
esac
if $if_up || [ "$reason" = ROUTERADVERT ]; then add_resolv_conf
elif $if_down; then remove_resolv_conf
fi
/lib/dhcpcd/dhcpcd-hooks >
cat 30-hostname
# Set the hostname from DHCP. Can be a short hostname or a FQDN.
# hostname_fqdn=true
# hostname_fqdn=false
# hostname_fqdn=server # 'server' means what the server says, don't manipulate it.
# RFC4702 section 3.1 says FQDN should be prefered over hostname.
#
# the default is hostname_fqdn=true so that a consistent hostname is always assigned.
: ${hostname_fqdn:=true}
# Some systems don't have hostname(1)
_hostname()
{ local name=
if [ -z "$1" ]; then
if type hostname >/dev/null 2>&1; then hostname
elif [ -r /proc/sys/kernel/hostname ]; then read name /dev/null 2>&1; then sysctl -n kern.hostname
elif sysctl kernel.hostname >/dev/null 2>&1; then sysctl -n kernel.hostname
else return 1
fi
return $?
fi
# prefer hostname(1)
if type hostname >/dev/null 2>&1; then hostname "$1"
elif [ -w /proc/sys/kernel/hostname ]; then echo "$1" >/proc/sys/kernel/hostname
elif sysctl kern.hostname >/dev/null 2>&1; then sysctl -w "kern.hostname=$1"
elif sysctl kernel.hostname >/dev/null 2>&1; then sysctl -w "kernel.hostname=$1"
else hostname "$1" # this will fail, with an error to stdout
fi
}
need_hostname()
{ local hostname hfqdn=false hshort=false
case "$force_hostname" in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) return 0;;
esac
hostname="$(_hostname)"
case "$hostname" in
""|"(none)"|localhost|localhost.localdomain) return 0;;
esac
case "$hostname_fqdn" in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;; #yestrue
""|[Ss][Ee][Rr][Vv][Ee][Rr]) ;; #server
*) hshort=true;;
esac
if [ -n "$old_fqdn" ]; then
if ${hfqdn} || ! ${hsort}; then [ "$hostname" = "$old_fqdn" ]
else [ "$hostname" = "${old_fqdn%%.*}" ]
fi
elif [ -n "$old_host_name" ]; then
if ${hfqdn}; then
if [ -n "$old_domain_name" -a \ "$old_host_name" = "${old_host_name#*.}" ]
then [ "$hostname" = \ "$old_host_name.$old_domain_name" ]
else [ "$hostname" = "$old_host_name" ]
fi
elif ${hshort}; then [ "$hostname" = "${old_host_name%%.*}" ]
else [ "$hostname" = "$old_host_name" ]
fi
else false # No old hostname
fi
}
try_hostname()
{ if valid_domainname "$1"; then _hostname "$1"
else syslog err "Invalid hostname: $1"
fi
}
set_hostname()
{ local hfqdn=false hshort=false
need_hostname || return
case "$hostname_fqdn" in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;; #yestrue
""|[Ss][Ee][Rr][Vv][Ee][Rr]) ;; #server
*) hshort=true;;
esac
if [ -n "$new_fqdn" ]; then
if ${hfqdn} || ! ${hshort}; then try_hostname "$new_fqdn"
else try_hostname "${new_fqdn%%.*}"
fi
elif [ -n "$new_host_name" ]; then
if ${hfqdn}; then
if [ -n "$new_domain_name" -a \ "$new_host_name" = "${new_host_name#*.}" ] then try_hostname "$new_host_name.$new_domain_name"
else try_hostname "$new_host_name"
fi
elif ${hshort}; then try_hostname "${new_host_name%%.*}"
else try_hostname "$new_host_name"
fi
fi
}
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
new_fqdn="$new_dhcp6_fqdn"
old_fqdn="$old_dhcp6_fqdn"
;;
esac
if $if_up; then set_hostname fi
/lib/dhcpcd/dhcpcd-hooks >
cat 50-ntp.conf
# Sample dhcpcd hook script for NTP
# configures one of NTP, OpenNTP or Chrony (in that order)
# and will default to NTP if no default config is found.
# Like resolv.conf hook , store a database of ntp.conf files and merge into /etc/ntp.conf
# set $NTP_CONF to override the derived default on systems with >1 NTP client installed.
# example for OpenNTP
# dhcpcd -e NTP_CONF=/usr/pkg/etc/ntpd.conf
# or by adding this to /etc/dhcpcd.conf
# env NTP_CONF=/usr/pkg/etc/ntpd.conf
# or by adding this to /etc/dhcpcd.enter-hook
# NTP_CONF=/usr/pkg/etc/ntpd.conf
# To use Chrony change ntpd.conf to chrony.conf in the above .
: ${ntp_confs:=ntp.conf ntpd.conf chrony.conf}
: ${ntp_conf_dirs=/etc /usr/pkg/etc /usr/local/etc}
ntp_conf_dir="$state_dir/ntp.conf"
# If NTP_CONF is not set, work out a good default
if [ -z "$NTP_CONF" ]; then
for d in ${ntp_conf_dirs}; do
for f in ${ntp_confs}; do
if [ -e "$d/$f" ]; then NTP_CONF="$d/$f" break 2 fi
done
done
[ -e "$NTP_CONF" ] || NTP_CONF=/etc/ntp.conf
fi
if [ -z "$ntp_service" ]; then # Derive service name from configuration
case "$NTP_CONF" in
*chrony.conf) ntp_service=chronyd;;
*) ntp_service=ntp;;
esac
fi
# Debian has a seperate file for DHCP config to avoid stamping on the master.
if [ "$ntp_service" = ntp ] && type invoke-rc.d >/dev/null 2>&1; then
[ -e /var/lib/ntp ] || mkdir /var/lib/ntp
: ${ntp_service:=ntp}
: ${NTP_DHCP_CONF:=/run/ntp.conf.dhcp}
fi
: ${ntp_restart_cmd:=service_condcommand $ntp_service restart}
ntp_conf=${NTP_CONF}
NL="
"
build_ntp_conf()
{
local cf="$state_dir/ntp.conf.$ifname"
local interfaces= header= srvs= servers= x=
# Build a list of interfaces
interfaces=$(list_interfaces "$ntp_conf_dir")
if [ -n "$interfaces" ]; then
for x in ${interfaces}; do header="$header${header:+, }$x" done # Build the header
# Build a server list
srvs=$(cd "$ntp_conf_dir";
key_get_value "server " $interfaces)
if [ -n "$srvs" ]; then
for x in $(uniqify $srvs); do servers="${servers}server $x$NL" done
fi
fi
# Merge our config into ntp.conf
[ -e "$cf" ] && rm -f "$cf"
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
if [ -n "$NTP_DHCP_CONF" ]; then
[ -e "$ntp_conf" ] && cp "$ntp_conf" "$cf"
ntp_conf="$NTP_DHCP_CONF"
elif [ -e "$ntp_conf" ]; then
remove_markers "$signature_base" "$signature_base_end" \
"$ntp_conf" > "$cf"
fi
if [ -n "$servers" ]; then
echo "$signature_base${header:+ $from }$header" >> "$cf"
printf %s "$servers" >> "$cf"
echo "$signature_base_end${header:+ $from }$header" >> "$cf"
else [ -e "$ntp_conf" -a -e "$cf" ] || return
fi
# If we changed anything, restart ntpd
if change_file "$ntp_conf" "$cf"; then [ -n "$ntp_restart_cmd" ] && eval $ntp_restart_cmd fi
}
add_ntp_conf()
{ local cf="$ntp_conf_dir/$ifname" x=
[ -e "$cf" ] && rm "$cf"
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
if [ -n "$new_ntp_servers" ]; then
for x in $new_ntp_servers; do echo "server $x" >> "$cf" done
fi
build_ntp_conf
}
remove_ntp_conf()
{ if [ -e "$ntp_conf_dir/$ifname" ]; then rm "$ntp_conf_dir/$ifname" fi
build_ntp_conf
}
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
new_ntp_servers="$new_dhcp6_sntp_servers"
;;
esac
if $if_up; then add_ntp_conf
elif $if_down; then remove_ntp_conf
fi
See /etc/dhcp/dhclient-exit-hooks.d
debug -> /etc/dhcp/debug
rfc3442-classless-routes
timesyncd
cat /etc/dhcp/dhclient-exit-hooks.d/timesyncd
TIMESYNCD_CONF=/run/systemd/timesyncd.conf.d/01-dhclient.conf
timesyncd_servers_setup_remove() {
if [ -e $TIMESYNCD_CONF ]; then
rm -f $TIMESYNCD_CONF
systemctl try-restart systemd-timesyncd.service || true
fi
}
timesyncd_servers_setup_add() {
if [ ! -d /run/systemd/system ]; then
return
fi
if [ -e $TIMESYNCD_CONF ] && [ "$new_ntp_servers" = "$old_ntp_servers" ]; then
return
fi
if [ -z "$new_ntp_servers" ]; then
timesyncd_servers_setup_remove
return
fi
mkdir -p $(dirname $TIMESYNCD_CONF)
cat < ${TIMESYNCD_CONF}.new
# NTP server entries received from DHCP server
[Time]
NTP=$new_ntp_servers
EOF
mv ${TIMESYNCD_CONF}.new ${TIMESYNCD_CONF}
systemctl try-restart systemd-timesyncd.service || true
}
case $reason in
BOUND|RENEW|REBIND|REBOOT)
timesyncd_servers_setup_add
;;
EXPIRE|FAIL|RELEASE|STOP)
timesyncd_servers_setup_remove
;;
esac