Home | History | Annotate | Line # | Download | only in hooks
      1      1.1  roy #!/bin/sh
      2  1.1.1.5  roy # SPDX-License-Identifier: BSD-2-Clause
      3  1.1.1.5  roy # Copyright (c) 2017-2022 Roy Marples <roy (at] marples.name>
      4  1.1.1.5  roy 
      5      1.1  roy # dhcpcd client configuration script 
      6      1.1  roy 
      7      1.1  roy # Handy variables and functions for our hooks to use
      8      1.1  roy ifname="$interface${protocol+.}$protocol"
      9      1.1  roy from=from
     10      1.1  roy signature_base="# Generated by dhcpcd"
     11      1.1  roy signature="$signature_base $from $ifname"
     12      1.1  roy signature_base_end="# End of dhcpcd"
     13      1.1  roy signature_end="$signature_base_end $from $ifname"
     14  1.1.1.4  roy state_dir=/var/run/dhcpcd/hook-state
     15      1.1  roy _detected_init=false
     16      1.1  roy 
     17      1.1  roy : ${if_up:=false}
     18      1.1  roy : ${if_down:=false}
     19      1.1  roy : ${syslog_debug:=false}
     20      1.1  roy 
     21      1.1  roy # Ensure that all arguments are unique
     22      1.1  roy uniqify()
     23      1.1  roy {
     24      1.1  roy 	result=
     25      1.1  roy 	for i do
     26      1.1  roy 		case " $result " in
     27      1.1  roy 			*" $i "*);;
     28      1.1  roy 			*) result="$result${result:+ }$i";;
     29      1.1  roy 		esac
     30      1.1  roy 	done
     31  1.1.1.5  roy 	printf '%s\n' "$result"
     32      1.1  roy }
     33      1.1  roy 
     34      1.1  roy # List interface config files in a directory.
     35      1.1  roy # If dhcpcd is running as a single instance then it will have a list of
     36      1.1  roy # interfaces in the preferred order.
     37      1.1  roy # Otherwise we just use what we have.
     38      1.1  roy list_interfaces()
     39      1.1  roy {
     40      1.1  roy 	ifaces=
     41      1.1  roy 	for i in $interface_order; do
     42      1.1  roy 		for x in "$1"/$i.*; do
     43      1.1  roy 			[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
     44      1.1  roy 		done
     45      1.1  roy 	done
     46      1.1  roy 	for x in "$1"/*; do
     47      1.1  roy 		[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
     48      1.1  roy 	done
     49      1.1  roy 	uniqify $ifaces
     50      1.1  roy }
     51      1.1  roy 
     52      1.1  roy # Trim function
     53      1.1  roy trim()
     54      1.1  roy {
     55      1.1  roy 	var="$*"
     56      1.1  roy 	var=${var#"${var%%[![:space:]]*}"}
     57      1.1  roy 	var=${var%"${var##*[![:space:]]}"}
     58      1.1  roy 	if [ -z "$var" ]; then
     59      1.1  roy 		# So it seems our shell doesn't support wctype(3) patterns
     60      1.1  roy 		# Fall back to sed
     61  1.1.1.5  roy 		var=$(printf '%s\n' "$*" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')
     62      1.1  roy 	fi
     63      1.1  roy 	printf %s "$var"
     64      1.1  roy }
     65      1.1  roy 
     66      1.1  roy # We normally use sed to extract values using a key from a list of files
     67      1.1  roy # but sed may not always be available at the time.
     68      1.1  roy key_get_value()
     69      1.1  roy {
     70      1.1  roy 	key="$1"
     71      1.1  roy 	shift
     72      1.1  roy 
     73  1.1.1.2  roy 	if command -v sed >/dev/null 2>&1; then
     74      1.1  roy 		sed -n "s/^$key//p" $@
     75      1.1  roy 	else
     76      1.1  roy 		for x do
     77      1.1  roy 			while read line; do
     78      1.1  roy 				case "$line" in
     79  1.1.1.5  roy 				"$key"*) printf '%s\n' "${line##$key}";;
     80      1.1  roy 				esac
     81      1.1  roy 			done < "$x"
     82      1.1  roy 		done
     83      1.1  roy 	fi
     84      1.1  roy }
     85      1.1  roy 
     86      1.1  roy # We normally use sed to remove markers from a configuration file
     87      1.1  roy # but sed may not always be available at the time.
     88      1.1  roy remove_markers()
     89      1.1  roy {
     90      1.1  roy 	m1="$1"
     91      1.1  roy 	m2="$2"
     92      1.1  roy 	in_marker=0
     93      1.1  roy 
     94      1.1  roy 	shift; shift
     95  1.1.1.2  roy 	if command -v sed >/dev/null 2>&1; then
     96      1.1  roy 		sed "/^$m1/,/^$m2/d" $@
     97      1.1  roy 	else
     98      1.1  roy 		for x do
     99      1.1  roy 			while read line; do
    100      1.1  roy 				case "$line" in
    101      1.1  roy 				"$m1"*) in_marker=1;;
    102      1.1  roy 				"$m2"*) in_marker=0;;
    103  1.1.1.5  roy 				*) [ $in_marker = 0 ] && printf '%s\n' "$line";;
    104      1.1  roy 				esac
    105      1.1  roy 			done < "$x"
    106      1.1  roy 		done
    107      1.1  roy 	fi
    108      1.1  roy }
    109      1.1  roy 
    110      1.1  roy # Compare two files.
    111      1.1  roy comp_file()
    112      1.1  roy {
    113      1.1  roy 	[ -e "$1" ] && [ -e "$2" ] || return 1
    114      1.1  roy 
    115  1.1.1.2  roy 	if command -v cmp >/dev/null 2>&1; then
    116      1.1  roy 		cmp -s "$1" "$2"
    117  1.1.1.2  roy 	elif command -v diff >/dev/null 2>&1; then
    118      1.1  roy 		diff -q "$1" "$2" >/dev/null
    119      1.1  roy 	else
    120      1.1  roy 		# Hopefully we're only working on small text files ...
    121      1.1  roy 		[ "$(cat "$1")" = "$(cat "$2")" ]
    122      1.1  roy 	fi
    123      1.1  roy }
    124      1.1  roy 
    125      1.1  roy # Compare two files.
    126      1.1  roy # If different, replace first with second otherwise remove second.
    127      1.1  roy change_file()
    128      1.1  roy {
    129      1.1  roy 	if [ -e "$1" ]; then
    130      1.1  roy 		if comp_file "$1" "$2"; then
    131      1.1  roy 			rm -f "$2"
    132      1.1  roy 			return 1
    133      1.1  roy 		fi
    134      1.1  roy 	fi
    135      1.1  roy 	cat "$2" > "$1"
    136      1.1  roy 	rm -f "$2"
    137      1.1  roy 	return 0
    138      1.1  roy }
    139      1.1  roy 
    140      1.1  roy # Compare two files.
    141      1.1  roy # If different, copy or link depending on target type
    142      1.1  roy copy_file()
    143      1.1  roy {
    144      1.1  roy 	if [ -h "$2" ]; then
    145      1.1  roy 		[ "$(readlink "$2")" = "$1" ] && return 1
    146      1.1  roy 		ln -sf "$1" "$2"
    147      1.1  roy 	else
    148      1.1  roy 		comp_file "$1" "$2" && return 1
    149      1.1  roy 		cat "$1" >"$2"
    150      1.1  roy 	fi
    151      1.1  roy }
    152      1.1  roy 
    153      1.1  roy # Save a config file
    154      1.1  roy save_conf()
    155      1.1  roy {
    156      1.1  roy 	if [ -f "$1" ]; then
    157      1.1  roy 		rm -f "$1-pre.$interface"
    158      1.1  roy 		cat "$1" > "$1-pre.$interface"
    159      1.1  roy 	fi
    160      1.1  roy }
    161      1.1  roy 
    162      1.1  roy # Restore a config file
    163      1.1  roy restore_conf()
    164      1.1  roy {
    165      1.1  roy 	[ -f "$1-pre.$interface" ] || return 1
    166      1.1  roy 	cat "$1-pre.$interface" > "$1"
    167      1.1  roy 	rm -f "$1-pre.$interface"
    168      1.1  roy }
    169      1.1  roy 
    170      1.1  roy # Write a syslog entry
    171      1.1  roy syslog()
    172      1.1  roy {
    173      1.1  roy 	lvl="$1"
    174      1.1  roy 
    175      1.1  roy 	if [ "$lvl" = debug ]; then
    176      1.1  roy 		${syslog_debug} || return 0
    177      1.1  roy 	fi
    178      1.1  roy 	[ -n "$lvl" ] && shift
    179      1.1  roy 	[ -n "$*" ] || return 0
    180      1.1  roy 	case "$lvl" in
    181  1.1.1.5  roy 	err|error)	printf '%s\n' "$interface: $*" >&2;;
    182  1.1.1.5  roy 	*)		printf '%s\n' "$interface: $*";;
    183      1.1  roy 	esac
    184  1.1.1.2  roy 	if command -v logger >/dev/null 2>&1; then
    185      1.1  roy 		logger -i -p daemon."$lvl" -t dhcpcd-run-hooks "$interface: $*"
    186      1.1  roy 	fi
    187      1.1  roy }
    188      1.1  roy 
    189      1.1  roy # Check for a valid name as per RFC952 and RFC1123 section 2.1
    190      1.1  roy valid_domainname()
    191      1.1  roy {
    192      1.1  roy 	name="$1"
    193      1.1  roy 	[ -z "$name" ] || [ ${#name} -gt 255 ] && return 1
    194      1.1  roy 	
    195      1.1  roy 	while [ -n "$name" ]; do
    196      1.1  roy 		label="${name%%.*}"
    197      1.1  roy 		[ -z "$label" ] || [ ${#label} -gt 63 ] && return 1
    198      1.1  roy 		case "$label" in
    199      1.1  roy 		-*|_*|*-|*_)		return 1;;
    200      1.1  roy 		*[![:alnum:]_-]*)	return 1;;
    201      1.1  roy 		"$name")		return 0;;
    202      1.1  roy 		esac
    203      1.1  roy 		name="${name#*.}"
    204      1.1  roy 	done
    205      1.1  roy 	return 0
    206      1.1  roy }
    207      1.1  roy 
    208      1.1  roy valid_domainname_list()
    209      1.1  roy {
    210      1.1  roy 	for name do
    211      1.1  roy 		valid_domainname "$name" || return $?
    212      1.1  roy 	done
    213      1.1  roy 	return 0
    214      1.1  roy }
    215      1.1  roy 
    216      1.1  roy # With the advent of alternative init systems, it's possible to have
    217      1.1  roy # more than one installed. So we need to try to guess what one we're
    218      1.1  roy # using unless overridden by configure.
    219      1.1  roy detect_init()
    220      1.1  roy {
    221      1.1  roy 	_service_exists=""
    222      1.1  roy 	_service_cmd=""
    223      1.1  roy 	_service_status=""
    224      1.1  roy 
    225      1.1  roy 	[ -n "$_service_cmd" ] && return 0
    226      1.1  roy 
    227      1.1  roy 	if $_detected_init; then
    228      1.1  roy 		[ -n "$_service_cmd" ]
    229      1.1  roy 		return $?
    230      1.1  roy 	fi
    231      1.1  roy 
    232      1.1  roy 	# Detect the running init system.
    233      1.1  roy 	# As systemd and OpenRC can be installed on top of legacy init
    234      1.1  roy 	# systems we try to detect them first.
    235      1.1  roy 	status=""
    236      1.1  roy 	: ${status:=status}
    237      1.1  roy 	if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
    238      1.1  roy 		_service_exists="/bin/systemctl --quiet is-enabled \$1.service"
    239      1.1  roy 		_service_status="/bin/systemctl --quiet is-active \$1.service"
    240  1.1.1.2  roy 		_service_cmd="/bin/systemctl \$2 --no-block \$1.service"
    241      1.1  roy 	elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
    242      1.1  roy 		_service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
    243      1.1  roy 		_service_status="/usr/bin/systemctl --quiet is-active \$1.service"
    244  1.1.1.2  roy 		_service_cmd="/usr/bin/systemctl \$2 --no-block \$1.service"
    245      1.1  roy 	elif [ -x /sbin/rc-service ] &&
    246      1.1  roy 	     { [ -s /libexec/rc/init.d/softlevel ] ||
    247      1.1  roy 	     [ -s /run/openrc/softlevel ]; }
    248      1.1  roy 	then
    249      1.1  roy 		_service_exists="/sbin/rc-service -e \$1"
    250      1.1  roy 		_service_cmd="/sbin/rc-service \$1 -- -D \$2"
    251      1.1  roy 	elif [ -x /usr/sbin/invoke-rc.d ]; then
    252      1.1  roy 		_service_exists="/usr/sbin/invoke-rc.d --query --quiet \$1 start >/dev/null 2>&1 || [ \$? = 104 ]"
    253      1.1  roy 		_service_cmd="/usr/sbin/invoke-rc.d \$1 \$2"
    254      1.1  roy 	elif [ -x /sbin/service ]; then
    255      1.1  roy 		_service_exists="/sbin/service \$1 >/dev/null 2>&1"
    256      1.1  roy 		_service_cmd="/sbin/service \$1 \$2"
    257      1.1  roy 	elif [ -x /usr/sbin/service ]; then
    258      1.1  roy 		_service_exists="/usr/sbin/service \$1 $status >/dev/null 2>&1"
    259      1.1  roy 		_service_cmd="/usr/sbin/service \$1 \$2"
    260      1.1  roy 	elif [ -x /bin/sv ]; then
    261      1.1  roy 		_service_exists="/bin/sv status \$1 >/dev/null 2>&1"
    262      1.1  roy 		_service_cmd="/bin/sv \$2 \$1"
    263      1.1  roy 	elif [ -x /usr/bin/sv ]; then
    264      1.1  roy 		_service_exists="/usr/bin/sv status \$1 >/dev/null 2>&1"
    265      1.1  roy 		_service_cmd="/usr/bin/sv \$2 \$1"
    266      1.1  roy 	elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then
    267      1.1  roy 		_service_exists="[ -x /etc/rc.d/rc.\$1 ]"
    268      1.1  roy 		_service_cmd="/etc/rc.d/rc.\$1 \$2"
    269      1.1  roy 		_service_status="/etc/rc.d/rc.\$1 status >/dev/null 2>&1"
    270      1.1  roy 	else
    271      1.1  roy 		for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
    272      1.1  roy 			if [ -d $x ]; then
    273      1.1  roy 				_service_exists="[ -x $x/\$1 ]"
    274      1.1  roy 				_service_cmd="$x/\$1 \$2"
    275      1.1  roy 				_service_status="$x/\$1 $status >/dev/null 2>&1"
    276      1.1  roy 				break
    277      1.1  roy 			fi
    278      1.1  roy 		done
    279      1.1  roy 		if [ -e /etc/arch-release ]; then
    280      1.1  roy 			_service_status="[ -e /var/run/daemons/\$1 ]"
    281      1.1  roy 		elif [ "$x" = "/etc/rc.d" ] && [ -e /etc/rc.d/rc.subr ]; then
    282      1.1  roy 			_service_status="$x/\$1 check >/dev/null 2>&1"
    283      1.1  roy 		fi
    284      1.1  roy 	fi
    285      1.1  roy 
    286      1.1  roy 	_detected_init=true
    287      1.1  roy 	if [ -z "$_service_cmd" ]; then
    288      1.1  roy 		syslog err "could not detect a useable init system"
    289      1.1  roy 		return 1
    290      1.1  roy 	fi
    291      1.1  roy 	return 0
    292      1.1  roy }
    293      1.1  roy 
    294      1.1  roy # Check a system service exists 
    295      1.1  roy service_exists()
    296      1.1  roy {
    297      1.1  roy 	if [ -z "$_service_exists" ]; then
    298      1.1  roy 		detect_init || return 1
    299      1.1  roy 	fi
    300      1.1  roy 	eval $_service_exists
    301      1.1  roy }
    302      1.1  roy 
    303      1.1  roy # Send a command to a system service
    304      1.1  roy service_cmd()
    305      1.1  roy {
    306      1.1  roy 	if [ -z "$_service_cmd" ]; then
    307      1.1  roy 		detect_init || return 1
    308      1.1  roy 	fi
    309      1.1  roy 	eval $_service_cmd
    310      1.1  roy }
    311      1.1  roy 
    312      1.1  roy # Send a command to a system service if it is running
    313      1.1  roy service_status()
    314      1.1  roy {
    315      1.1  roy 	if [ -z "$_service_cmd" ]; then
    316      1.1  roy 		detect_init || return 1
    317      1.1  roy 	fi
    318      1.1  roy 	if [ -n "$_service_status" ]; then
    319      1.1  roy 		eval $_service_status
    320      1.1  roy 	else
    321      1.1  roy 		service_command $1 status >/dev/null 2>&1
    322      1.1  roy 	fi
    323      1.1  roy }
    324      1.1  roy 
    325      1.1  roy # Handy macros for our hooks
    326      1.1  roy service_command()
    327      1.1  roy {
    328      1.1  roy 	service_exists $1 && service_cmd $1 $2
    329      1.1  roy }
    330      1.1  roy service_condcommand()
    331      1.1  roy {
    332      1.1  roy 	service_exists $1 && service_status $1 && service_cmd $1 $2
    333      1.1  roy }
    334      1.1  roy 
    335      1.1  roy # We source each script into this one so that scripts run earlier can
    336      1.1  roy # remove variables from the environment so later scripts don't see them.
    337      1.1  roy # Thus, the user can create their dhcpcd.enter/exit-hook script to configure
    338      1.1  roy # /etc/resolv.conf how they want and stop the system scripts ever updating it.
    339      1.1  roy for hook in \
    340  1.1.1.4  roy 	/etc/dhcpcd.enter-hook \
    341  1.1.1.4  roy 	/libexec/dhcpcd-hooks/* \
    342  1.1.1.4  roy 	/etc/dhcpcd.exit-hook
    343      1.1  roy do
    344  1.1.1.2  roy 	case "$hook" in
    345  1.1.1.2  roy 		*/*~)	continue;;
    346  1.1.1.2  roy 	esac
    347      1.1  roy 	for skip in $skip_hooks; do
    348      1.1  roy 		case "$hook" in
    349      1.1  roy 			*/"$skip")			continue 2;;
    350      1.1  roy 			*/[0-9][0-9]"-$skip")		continue 2;;
    351      1.1  roy 			*/[0-9][0-9]"-$skip.sh")	continue 2;;
    352      1.1  roy 		esac
    353      1.1  roy 	done
    354      1.1  roy 	if [ -f "$hook" ]; then
    355      1.1  roy 		. "$hook"
    356      1.1  roy 	fi
    357      1.1  roy done
    358