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