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