#!/bin/sh # # This file is part of the resolvconf package. # set -e echo_usage() { cat <<-EOF Usage: resolvconf COMMAND Manage nameserver information. Commands: -a IFACE Add DNS configuration for IFACE from stdin. -d IFACE Delete DNS configuration for IFACE. -u Run update actions (e.g. regenerate resolv.conf). -i List interfaces providing DNS configuration. --list, -l Show DNS configuration for each interface. --enable-updates Enable updates; run update actions if any are pending. --disable-updates Disable updates until re-enabled. --updates-are-enabled Return 0 if updates are enabled, 1 otherwise. --version Print the resolvconf version. --help, -h Print this help text. The IFACE argument may not contain spaces, slashes, an initial dot, an initial hyphen or an initial tilde. IFACE is conventionally formed from the interface name, a dot, and the name of the interface configuration program (e.g. dhclient) or the address family name (e.g. inet or inet6): eth0.dhclient, wlan0.inet6 etc. EOF } VERSION="Git snapshot" PATH=/sbin:/bin:/usr/sbin:/usr/bin MYNAME="${0##*/}" RUN_DIR=/run/resolvconf ENABLE_UPDATES_FLAGFILE="${RUN_DIR}/enable-updates" POSTPONED_UPDATE_FLAGFILE="${RUN_DIR}/postponed-update" report_err() { echo "${MYNAME}: Error: $*" >&2 ; } # Check arguments CMD="$1" case "$CMD" in -a|-d) IFACE="$2" if [ ! "$IFACE" ] ; then report_err "No interface name specified" echo_usage >&2 exit 1 fi report_iface_err() { report_err "$* not allowed in interface record name" } case "$IFACE" in */*) report_iface_err "Slash" ; exit 1 ;; *" "*) report_iface_err "Space" ; exit 1 ;; .*) report_iface_err "Initial dot" ; exit 1 ;; -*) report_iface_err "Initial hyphen" ; exit 1 ;; \~*) report_iface_err "Initial tilde" ; exit 1 ;; esac ;; -u|--enable-updates|--disable-updates|--updates-are-enabled) if [ "$2" ] ; then report_err "The $CMD option does not take an argument" echo_usage >&2 exit 1 fi ;; --version) echo "Debian resolvconf $VERSION" if [ -r /usr/share/doc/resolvconf/copyright ] then echo "Copyright: " sed -n '/Copyright:/,${ s/Copyright[: ]//g; /^$/d; /^License: /{p;q}; s/^ */ /; p}' \ /usr/share/doc/resolvconf/copyright fi exit 0 ;; --help|-h) echo_usage >&2 exit 0 ;; --list|-l) ;; -i) echo $(/lib/resolvconf/list-records) exit 0 ;; *) report_err "Command not recognized" echo_usage >&2 exit 99 ;; esac [ -d "${RUN_DIR}/interface" ] || { mkdir -p "${RUN_DIR}/interface" ; } [ -d "${RUN_DIR}/interface" ] || { report_err "${RUN_DIR}/interface either does not exist or is not a directory" ; exit 1 ; } cd "${RUN_DIR}/interface" { # Make sure that we do not end up with a broken resolv.conf if resolvconf # is invoked in parallel. LOCK_WAIT_S=10 # BRINTOS-WORKAROUND: brintos/linux#40 remove -- util-linux's `flock -w` arms # its timeout with timer_create(2), which is ENOSYS on wasm32: syscall slots # 222..226 are absent from the table even though CONFIG_POSIX_TIMERS=y and # posix-timers.o is in vmlinux. flock then exits 71 having never attempted the # lock at all, so EVERY state-changing resolvconf command fails and reports # "Run lock held by another process" about a lock nothing was holding. # # The fallback keeps the SAME bounded-wait contract using only `flock -n`, # which is LOCK_NB through fcntl(2) and needs no timer. It costs nothing where # -w works: the condition is false and the branch is never entered, which is # why this is a fallback rather than a replacement. # # Delete this whole branch and restore the bare `flock -w` when 40 lands. if ! flock -w $LOCK_WAIT_S 9 2>/dev/null ; then LOCK_WAITED_S=0 while ! flock -n 9 ; do LOCK_WAITED_S=$((LOCK_WAITED_S + 1)) if [ $LOCK_WAITED_S -ge $LOCK_WAIT_S ] ; then report_err "Run lock held by another process for longer than $LOCK_WAIT_S seconds" exit 1 fi sleep 1 done fi update_and_exit() { rm -f "$POSTPONED_UPDATE_FLAGFILE" exec run-parts ${1:+--arg="$1"} ${2:+--arg="$2"} /etc/resolvconf/update.d } # Handle commands that don't require normalized stdin case "$CMD" in -d) if [ ! -e "$IFACE" ] ; then exit 0 fi if [ ! -s "$IFACE" ] ; then rm -f "$IFACE" exit 0 fi rm -f "$IFACE" if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then update_and_exit -d "$IFACE" else : >| "$POSTPONED_UPDATE_FLAGFILE" exit 0 fi ;; --list|-l) for f in $(/lib/resolvconf/list-records) do echo "# resolv.conf from $f" cat $f done exit 0 ;; -u) if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then update_and_exit -u else : >| "$POSTPONED_UPDATE_FLAGFILE" exit 0 fi ;; --enable-updates) : >| "$ENABLE_UPDATES_FLAGFILE" || exit 1 if [ -e "$POSTPONED_UPDATE_FLAGFILE" ] ; then (update_and_exit -u) || : fi exit 0 ;; --disable-updates) rm -f "$POSTPONED_UPDATE_FLAGFILE" || : rm -f "$ENABLE_UPDATES_FLAGFILE" || exit 1 exit 0 ;; --updates-are-enabled) if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then exit 0 else exit 1 fi ;; esac case "$CMD" in -a) OLD_CONTENT="" [ -f "$IFACE" ] && OLD_CONTENT="$(cat "$IFACE")" NEW_CONTENT="$(/lib/resolvconf/normalize-resolvconf)" # Proceed only if content has changed. The test here can't # eliminate 100% of redundant invocations of update scripts # because we don't do any locking; however it certainly does # eliminate most of them. if [ "$NEW_CONTENT" = "$OLD_CONTENT" ] ; then exit 0 fi IFACE_TMPFILE="${IFACE}_new.$$" cleanup() { rm -f "$IFACE_TMPFILE" ; } trap cleanup EXIT echo "$NEW_CONTENT" > "$IFACE_TMPFILE" mv -f "$IFACE_TMPFILE" "$IFACE" if [ -e "$ENABLE_UPDATES_FLAGFILE" ] ; then update_and_exit -a "$IFACE" else : >| "$POSTPONED_UPDATE_FLAGFILE" exit 0 fi ;; esac # Don't reach here exit 99 } 9>"${RUN_DIR}/run-lock"