10a68f8d2SRoy Marples#!/bin/sh
20a68f8d2SRoy Marples# dhcpcd client configuration script
30a68f8d2SRoy Marples
40a68f8d2SRoy Marples# Handy variables and functions for our hooks to use
50a68f8d2SRoy Marplesifname="$interface${protocol+.}$protocol"
60a68f8d2SRoy Marplesfrom=from
70a68f8d2SRoy Marplessignature_base="# Generated by dhcpcd"
80a68f8d2SRoy Marplessignature="$signature_base $from $ifname"
90a68f8d2SRoy Marplessignature_base_end="# End of dhcpcd"
100a68f8d2SRoy Marplessignature_end="$signature_base_end $from $ifname"
110a68f8d2SRoy Marplesstate_dir=/var/run/dhcpcd/hook-state
120a68f8d2SRoy Marples_detected_init=false
130a68f8d2SRoy Marples
140a68f8d2SRoy Marples: ${if_up:=false}
150a68f8d2SRoy Marples: ${if_down:=false}
160a68f8d2SRoy Marples: ${syslog_debug:=false}
170a68f8d2SRoy Marples
180a68f8d2SRoy Marples# Ensure that all arguments are unique
190a68f8d2SRoy Marplesuniqify()
200a68f8d2SRoy Marples{
210a68f8d2SRoy Marples	result=
220a68f8d2SRoy Marples	for i do
230a68f8d2SRoy Marples		case " $result " in
240a68f8d2SRoy Marples			*" $i "*);;
250a68f8d2SRoy Marples			*) result="$result${result:+ }$i";;
260a68f8d2SRoy Marples		esac
270a68f8d2SRoy Marples	done
280a68f8d2SRoy Marples	echo "$result"
290a68f8d2SRoy Marples}
300a68f8d2SRoy Marples
310a68f8d2SRoy Marples# List interface config files in a directory.
320a68f8d2SRoy Marples# If dhcpcd is running as a single instance then it will have a list of
330a68f8d2SRoy Marples# interfaces in the preferred order.
340a68f8d2SRoy Marples# Otherwise we just use what we have.
350a68f8d2SRoy Marpleslist_interfaces()
360a68f8d2SRoy Marples{
370a68f8d2SRoy Marples	ifaces=
380a68f8d2SRoy Marples	for i in $interface_order; do
390a68f8d2SRoy Marples		for x in "$1"/$i.*; do
400a68f8d2SRoy Marples			[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
410a68f8d2SRoy Marples		done
420a68f8d2SRoy Marples	done
430a68f8d2SRoy Marples	for x in "$1"/*; do
440a68f8d2SRoy Marples		[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
450a68f8d2SRoy Marples	done
460a68f8d2SRoy Marples	uniqify $ifaces
470a68f8d2SRoy Marples}
480a68f8d2SRoy Marples
490a68f8d2SRoy Marples# Trim function
500a68f8d2SRoy Marplestrim()
510a68f8d2SRoy Marples{
520a68f8d2SRoy Marples	var="$*"
530a68f8d2SRoy Marples	var=${var#"${var%%[![:space:]]*}"}
540a68f8d2SRoy Marples	var=${var%"${var##*[![:space:]]}"}
550a68f8d2SRoy Marples	if [ -z "$var" ]; then
560a68f8d2SRoy Marples		# So it seems our shell doesn't support wctype(3) patterns
570a68f8d2SRoy Marples		# Fall back to sed
580a68f8d2SRoy Marples		var=$(echo "$*" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')
590a68f8d2SRoy Marples	fi
600a68f8d2SRoy Marples	printf %s "$var"
610a68f8d2SRoy Marples}
620a68f8d2SRoy Marples
630a68f8d2SRoy Marples# We normally use sed to extract values using a key from a list of files
640a68f8d2SRoy Marples# but sed may not always be available at the time.
650a68f8d2SRoy Marpleskey_get_value()
660a68f8d2SRoy Marples{
670a68f8d2SRoy Marples	key="$1"
680a68f8d2SRoy Marples	shift
690a68f8d2SRoy Marples
70*80aa9461SRoy Marples	if command -v sed >/dev/null 2>&1; then
710a68f8d2SRoy Marples		sed -n "s/^$key//p" $@
720a68f8d2SRoy Marples	else
730a68f8d2SRoy Marples		for x do
740a68f8d2SRoy Marples			while read line; do
750a68f8d2SRoy Marples				case "$line" in
760a68f8d2SRoy Marples				"$key"*) echo "${line##$key}";;
770a68f8d2SRoy Marples				esac
780a68f8d2SRoy Marples			done < "$x"
790a68f8d2SRoy Marples		done
800a68f8d2SRoy Marples	fi
810a68f8d2SRoy Marples}
820a68f8d2SRoy Marples
830a68f8d2SRoy Marples# We normally use sed to remove markers from a configuration file
840a68f8d2SRoy Marples# but sed may not always be available at the time.
850a68f8d2SRoy Marplesremove_markers()
860a68f8d2SRoy Marples{
870a68f8d2SRoy Marples	m1="$1"
880a68f8d2SRoy Marples	m2="$2"
890a68f8d2SRoy Marples	in_marker=0
900a68f8d2SRoy Marples
910a68f8d2SRoy Marples	shift; shift
92*80aa9461SRoy Marples	if command -v sed >/dev/null 2>&1; then
930a68f8d2SRoy Marples		sed "/^$m1/,/^$m2/d" $@
940a68f8d2SRoy Marples	else
950a68f8d2SRoy Marples		for x do
960a68f8d2SRoy Marples			while read line; do
970a68f8d2SRoy Marples				case "$line" in
980a68f8d2SRoy Marples				"$m1"*) in_marker=1;;
990a68f8d2SRoy Marples				"$m2"*) in_marker=0;;
1000a68f8d2SRoy Marples				*) [ $in_marker = 0 ] && echo "$line";;
1010a68f8d2SRoy Marples				esac
1020a68f8d2SRoy Marples			done < "$x"
1030a68f8d2SRoy Marples		done
1040a68f8d2SRoy Marples	fi
1050a68f8d2SRoy Marples}
1060a68f8d2SRoy Marples
1070a68f8d2SRoy Marples# Compare two files.
1080a68f8d2SRoy Marplescomp_file()
1090a68f8d2SRoy Marples{
1100a68f8d2SRoy Marples	[ -e "$1" ] && [ -e "$2" ] || return 1
1110a68f8d2SRoy Marples
112*80aa9461SRoy Marples	if command -v cmp >/dev/null 2>&1; then
1130a68f8d2SRoy Marples		cmp -s "$1" "$2"
114*80aa9461SRoy Marples	elif command -v diff >/dev/null 2>&1; then
1150a68f8d2SRoy Marples		diff -q "$1" "$2" >/dev/null
1160a68f8d2SRoy Marples	else
1170a68f8d2SRoy Marples		# Hopefully we're only working on small text files ...
1180a68f8d2SRoy Marples		[ "$(cat "$1")" = "$(cat "$2")" ]
1190a68f8d2SRoy Marples	fi
1200a68f8d2SRoy Marples}
1210a68f8d2SRoy Marples
1220a68f8d2SRoy Marples# Compare two files.
1230a68f8d2SRoy Marples# If different, replace first with second otherwise remove second.
1240a68f8d2SRoy Marpleschange_file()
1250a68f8d2SRoy Marples{
1260a68f8d2SRoy Marples	if [ -e "$1" ]; then
1270a68f8d2SRoy Marples		if comp_file "$1" "$2"; then
1280a68f8d2SRoy Marples			rm -f "$2"
1290a68f8d2SRoy Marples			return 1
1300a68f8d2SRoy Marples		fi
1310a68f8d2SRoy Marples	fi
1320a68f8d2SRoy Marples	cat "$2" > "$1"
1330a68f8d2SRoy Marples	rm -f "$2"
1340a68f8d2SRoy Marples	return 0
1350a68f8d2SRoy Marples}
1360a68f8d2SRoy Marples
1370a68f8d2SRoy Marples# Compare two files.
1380a68f8d2SRoy Marples# If different, copy or link depending on target type
1390a68f8d2SRoy Marplescopy_file()
1400a68f8d2SRoy Marples{
1410a68f8d2SRoy Marples	if [ -h "$2" ]; then
1420a68f8d2SRoy Marples		[ "$(readlink "$2")" = "$1" ] && return 1
1430a68f8d2SRoy Marples		ln -sf "$1" "$2"
1440a68f8d2SRoy Marples	else
1450a68f8d2SRoy Marples		comp_file "$1" "$2" && return 1
1460a68f8d2SRoy Marples		cat "$1" >"$2"
1470a68f8d2SRoy Marples	fi
1480a68f8d2SRoy Marples}
1490a68f8d2SRoy Marples
1500a68f8d2SRoy Marples# Save a config file
1510a68f8d2SRoy Marplessave_conf()
1520a68f8d2SRoy Marples{
1530a68f8d2SRoy Marples	if [ -f "$1" ]; then
1540a68f8d2SRoy Marples		rm -f "$1-pre.$interface"
1550a68f8d2SRoy Marples		cat "$1" > "$1-pre.$interface"
1560a68f8d2SRoy Marples	fi
1570a68f8d2SRoy Marples}
1580a68f8d2SRoy Marples
1590a68f8d2SRoy Marples# Restore a config file
1600a68f8d2SRoy Marplesrestore_conf()
1610a68f8d2SRoy Marples{
1620a68f8d2SRoy Marples	[ -f "$1-pre.$interface" ] || return 1
1630a68f8d2SRoy Marples	cat "$1-pre.$interface" > "$1"
1640a68f8d2SRoy Marples	rm -f "$1-pre.$interface"
1650a68f8d2SRoy Marples}
1660a68f8d2SRoy Marples
1670a68f8d2SRoy Marples# Write a syslog entry
1680a68f8d2SRoy Marplessyslog()
1690a68f8d2SRoy Marples{
1700a68f8d2SRoy Marples	lvl="$1"
1710a68f8d2SRoy Marples
1720a68f8d2SRoy Marples	if [ "$lvl" = debug ]; then
1730a68f8d2SRoy Marples		${syslog_debug} || return 0
1740a68f8d2SRoy Marples	fi
1750a68f8d2SRoy Marples	[ -n "$lvl" ] && shift
1760a68f8d2SRoy Marples	[ -n "$*" ] || return 0
1770a68f8d2SRoy Marples	case "$lvl" in
1780a68f8d2SRoy Marples	err|error)	echo "$interface: $*" >&2;;
1790a68f8d2SRoy Marples	*)		echo "$interface: $*";;
1800a68f8d2SRoy Marples	esac
181*80aa9461SRoy Marples	if command -v logger >/dev/null 2>&1; then
1820a68f8d2SRoy Marples		logger -i -p daemon."$lvl" -t dhcpcd-run-hooks "$interface: $*"
1830a68f8d2SRoy Marples	fi
1840a68f8d2SRoy Marples}
1850a68f8d2SRoy Marples
1860a68f8d2SRoy Marples# Check for a valid name as per RFC952 and RFC1123 section 2.1
1870a68f8d2SRoy Marplesvalid_domainname()
1880a68f8d2SRoy Marples{
1890a68f8d2SRoy Marples	name="$1"
1900a68f8d2SRoy Marples	[ -z "$name" ] || [ ${#name} -gt 255 ] && return 1
1910a68f8d2SRoy Marples
1920a68f8d2SRoy Marples	while [ -n "$name" ]; do
1930a68f8d2SRoy Marples		label="${name%%.*}"
1940a68f8d2SRoy Marples		[ -z "$label" ] || [ ${#label} -gt 63 ] && return 1
1950a68f8d2SRoy Marples		case "$label" in
1960a68f8d2SRoy Marples		-*|_*|*-|*_)		return 1;;
1970a68f8d2SRoy Marples		*[![:alnum:]_-]*)	return 1;;
1980a68f8d2SRoy Marples		"$name")		return 0;;
1990a68f8d2SRoy Marples		esac
2000a68f8d2SRoy Marples		name="${name#*.}"
2010a68f8d2SRoy Marples	done
2020a68f8d2SRoy Marples	return 0
2030a68f8d2SRoy Marples}
2040a68f8d2SRoy Marples
2050a68f8d2SRoy Marplesvalid_domainname_list()
2060a68f8d2SRoy Marples{
2070a68f8d2SRoy Marples	for name do
2080a68f8d2SRoy Marples		valid_domainname "$name" || return $?
2090a68f8d2SRoy Marples	done
2100a68f8d2SRoy Marples	return 0
2110a68f8d2SRoy Marples}
2120a68f8d2SRoy Marples
2130a68f8d2SRoy Marples# With the advent of alternative init systems, it's possible to have
2140a68f8d2SRoy Marples# more than one installed. So we need to try to guess what one we're
2150a68f8d2SRoy Marples# using unless overridden by configure.
2160a68f8d2SRoy Marplesdetect_init()
2170a68f8d2SRoy Marples{
2180a68f8d2SRoy Marples	_service_exists=""
2190a68f8d2SRoy Marples	_service_cmd=""
2200a68f8d2SRoy Marples	_service_status=""
2210a68f8d2SRoy Marples
2220a68f8d2SRoy Marples	[ -n "$_service_cmd" ] && return 0
2230a68f8d2SRoy Marples
2240a68f8d2SRoy Marples	if $_detected_init; then
2250a68f8d2SRoy Marples		[ -n "$_service_cmd" ]
2260a68f8d2SRoy Marples		return $?
2270a68f8d2SRoy Marples	fi
2280a68f8d2SRoy Marples
2290a68f8d2SRoy Marples	# Detect the running init system.
2300a68f8d2SRoy Marples	# As systemd and OpenRC can be installed on top of legacy init
2310a68f8d2SRoy Marples	# systems we try to detect them first.
2320a68f8d2SRoy Marples	status="onestatus"
2330a68f8d2SRoy Marples	: ${status:=status}
2340a68f8d2SRoy Marples	if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
2350a68f8d2SRoy Marples		_service_exists="/bin/systemctl --quiet is-enabled \$1.service"
2360a68f8d2SRoy Marples		_service_status="/bin/systemctl --quiet is-active \$1.service"
237*80aa9461SRoy Marples		_service_cmd="/bin/systemctl \$2 --no-block \$1.service"
2380a68f8d2SRoy Marples	elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
2390a68f8d2SRoy Marples		_service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
2400a68f8d2SRoy Marples		_service_status="/usr/bin/systemctl --quiet is-active \$1.service"
241*80aa9461SRoy Marples		_service_cmd="/usr/bin/systemctl \$2 --no-block \$1.service"
2420a68f8d2SRoy Marples	elif [ -x /sbin/rc-service ] &&
2430a68f8d2SRoy Marples	     { [ -s /libexec/rc/init.d/softlevel ] ||
2440a68f8d2SRoy Marples	     [ -s /run/openrc/softlevel ]; }
2450a68f8d2SRoy Marples	then
2460a68f8d2SRoy Marples		_service_exists="/sbin/rc-service -e \$1"
2470a68f8d2SRoy Marples		_service_cmd="/sbin/rc-service \$1 -- -D \$2"
2480a68f8d2SRoy Marples	elif [ -x /usr/sbin/invoke-rc.d ]; then
2490a68f8d2SRoy Marples		_service_exists="/usr/sbin/invoke-rc.d --query --quiet \$1 start >/dev/null 2>&1 || [ \$? = 104 ]"
2500a68f8d2SRoy Marples		_service_cmd="/usr/sbin/invoke-rc.d \$1 \$2"
2510a68f8d2SRoy Marples	elif [ -x /sbin/service ]; then
2520a68f8d2SRoy Marples		_service_exists="/sbin/service \$1 >/dev/null 2>&1"
2530a68f8d2SRoy Marples		_service_cmd="/sbin/service \$1 \$2"
2540a68f8d2SRoy Marples	elif [ -x /usr/sbin/service ]; then
2550a68f8d2SRoy Marples		_service_exists="/usr/sbin/service \$1 $status >/dev/null 2>&1"
2560a68f8d2SRoy Marples		_service_cmd="/usr/sbin/service \$1 \$2"
2570a68f8d2SRoy Marples	elif [ -x /bin/sv ]; then
2580a68f8d2SRoy Marples		_service_exists="/bin/sv status \$1 >/dev/null 2>&1"
2590a68f8d2SRoy Marples		_service_cmd="/bin/sv \$2 \$1"
2600a68f8d2SRoy Marples	elif [ -x /usr/bin/sv ]; then
2610a68f8d2SRoy Marples		_service_exists="/usr/bin/sv status \$1 >/dev/null 2>&1"
2620a68f8d2SRoy Marples		_service_cmd="/usr/bin/sv \$2 \$1"
2630a68f8d2SRoy Marples	elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then
2640a68f8d2SRoy Marples		_service_exists="[ -x /etc/rc.d/rc.\$1 ]"
2650a68f8d2SRoy Marples		_service_cmd="/etc/rc.d/rc.\$1 \$2"
2660a68f8d2SRoy Marples		_service_status="/etc/rc.d/rc.\$1 status >/dev/null 2>&1"
2670a68f8d2SRoy Marples	else
2680a68f8d2SRoy Marples		for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
2690a68f8d2SRoy Marples			if [ -d $x ]; then
2700a68f8d2SRoy Marples				_service_exists="[ -x $x/\$1 ]"
2710a68f8d2SRoy Marples				_service_cmd="$x/\$1 \$2"
2720a68f8d2SRoy Marples				_service_status="$x/\$1 $status >/dev/null 2>&1"
2730a68f8d2SRoy Marples				break
2740a68f8d2SRoy Marples			fi
2750a68f8d2SRoy Marples		done
2760a68f8d2SRoy Marples		if [ -e /etc/arch-release ]; then
2770a68f8d2SRoy Marples			_service_status="[ -e /var/run/daemons/\$1 ]"
2780a68f8d2SRoy Marples		elif [ "$x" = "/etc/rc.d" ] && [ -e /etc/rc.d/rc.subr ]; then
2790a68f8d2SRoy Marples			_service_status="$x/\$1 check >/dev/null 2>&1"
2800a68f8d2SRoy Marples		fi
2810a68f8d2SRoy Marples	fi
2820a68f8d2SRoy Marples
2830a68f8d2SRoy Marples	_detected_init=true
2840a68f8d2SRoy Marples	if [ -z "$_service_cmd" ]; then
2850a68f8d2SRoy Marples		syslog err "could not detect a useable init system"
2860a68f8d2SRoy Marples		return 1
2870a68f8d2SRoy Marples	fi
2880a68f8d2SRoy Marples	return 0
2890a68f8d2SRoy Marples}
2900a68f8d2SRoy Marples
2910a68f8d2SRoy Marples# Check a system service exists
2920a68f8d2SRoy Marplesservice_exists()
2930a68f8d2SRoy Marples{
2940a68f8d2SRoy Marples	if [ -z "$_service_exists" ]; then
2950a68f8d2SRoy Marples		detect_init || return 1
2960a68f8d2SRoy Marples	fi
2970a68f8d2SRoy Marples	eval $_service_exists
2980a68f8d2SRoy Marples}
2990a68f8d2SRoy Marples
3000a68f8d2SRoy Marples# Send a command to a system service
3010a68f8d2SRoy Marplesservice_cmd()
3020a68f8d2SRoy Marples{
3030a68f8d2SRoy Marples	if [ -z "$_service_cmd" ]; then
3040a68f8d2SRoy Marples		detect_init || return 1
3050a68f8d2SRoy Marples	fi
3060a68f8d2SRoy Marples	eval $_service_cmd
3070a68f8d2SRoy Marples}
3080a68f8d2SRoy Marples
3090a68f8d2SRoy Marples# Send a command to a system service if it is running
3100a68f8d2SRoy Marplesservice_status()
3110a68f8d2SRoy Marples{
3120a68f8d2SRoy Marples	if [ -z "$_service_cmd" ]; then
3130a68f8d2SRoy Marples		detect_init || return 1
3140a68f8d2SRoy Marples	fi
3150a68f8d2SRoy Marples	if [ -n "$_service_status" ]; then
3160a68f8d2SRoy Marples		eval $_service_status
3170a68f8d2SRoy Marples	else
3180a68f8d2SRoy Marples		service_command $1 status >/dev/null 2>&1
3190a68f8d2SRoy Marples	fi
3200a68f8d2SRoy Marples}
3210a68f8d2SRoy Marples
3220a68f8d2SRoy Marples# Handy macros for our hooks
3230a68f8d2SRoy Marplesservice_command()
3240a68f8d2SRoy Marples{
3250a68f8d2SRoy Marples	service_exists $1 && service_cmd $1 $2
3260a68f8d2SRoy Marples}
3270a68f8d2SRoy Marplesservice_condcommand()
3280a68f8d2SRoy Marples{
3290a68f8d2SRoy Marples	service_exists $1 && service_status $1 && service_cmd $1 $2
3300a68f8d2SRoy Marples}
3310a68f8d2SRoy Marples
3320a68f8d2SRoy Marples# We source each script into this one so that scripts run earlier can
3330a68f8d2SRoy Marples# remove variables from the environment so later scripts don't see them.
3340a68f8d2SRoy Marples# Thus, the user can create their dhcpcd.enter/exit-hook script to configure
3350a68f8d2SRoy Marples# /etc/resolv.conf how they want and stop the system scripts ever updating it.
3360a68f8d2SRoy Marplesfor hook in \
3370a68f8d2SRoy Marples	/etc/dhcpcd.enter-hook \
3380a68f8d2SRoy Marples	/usr/libexec/dhcpcd-hooks/* \
3390a68f8d2SRoy Marples	/etc/dhcpcd.exit-hook
3400a68f8d2SRoy Marplesdo
341*80aa9461SRoy Marples	case "$hook" in
342*80aa9461SRoy Marples		*/*~)	continue;;
343*80aa9461SRoy Marples	esac
3440a68f8d2SRoy Marples	for skip in $skip_hooks; do
3450a68f8d2SRoy Marples		case "$hook" in
3460a68f8d2SRoy Marples			*/"$skip")			continue 2;;
3470a68f8d2SRoy Marples			*/[0-9][0-9]"-$skip")		continue 2;;
3480a68f8d2SRoy Marples			*/[0-9][0-9]"-$skip.sh")	continue 2;;
3490a68f8d2SRoy Marples		esac
3500a68f8d2SRoy Marples	done
3510a68f8d2SRoy Marples	if [ -f "$hook" ]; then
3520a68f8d2SRoy Marples		. "$hook"
3530a68f8d2SRoy Marples	fi
3540a68f8d2SRoy Marplesdone
355