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