. "$BST_LIB" require ifup ifupdown/present # ifupdown ships ifup and ifdown (a hard link to the same binary, dispatching on # argv[0]) plus the interfaces(5) parser. Everything below drives a sample # interfaces file in a scratch dir via -i, with --state-dir pointing at that same # scratch dir, so the suite never reads or writes the live /etc/network/interfaces # or /run/network/ifstate. --no-scripts keeps the /etc/network/if-*.d hooks out: # run-parts hooks are not part of what this port delivers. tmp=${TMPDIR:-/tmp}/bst-ifupdown.$$ mkdir -p "$tmp" cfg="$tmp/interfaces" IF="ifup -i $cfg --state-dir $tmp --no-scripts" ID="ifdown -i $cfg --state-dir $tmp --no-scripts" cat > "$cfg" <<'EOF' auto lo iface lo inet loopback allow-hotplug bst0 iface bst0 inet static address 10.99.0.1 netmask 255.255.255.0 EOF command -v ifdown >/dev/null 2>&1 \ && bst_ok ifupdown/ifdown-present \ || bst_notok ifupdown/ifdown-present "ifdown missing (recipe installs it as a hard link to ifup)" # `ifup -V` prints " version " -- the binary names itself, not the # package, so the needle is "ifup version" and not "ifupdown". check_out ifupdown/version "ifup version" sh -c 'ifup -V 2>&1' # --- interfaces(5) parsing, via the dry run (-n) ----------------------------- # With -n, execute.c prints each command it *would* run to stderr and runs # nothing, so this asserts the parse and the address->command conversion without # needing the interface to exist. check ifupdown/parse-dryrun sh -c "$IF -n bst0 >/dev/null 2>&1" check_out ifupdown/dryrun-addr "10.99.0.1" sh -c "$IF -n bst0 2>&1" check_out ifupdown/dryrun-mask "10.99.0.1/255.255.255.0" sh -c "$IF -n bst0 2>&1" check_out ifupdown/dryrun-link "ip link set dev bst0" sh -c "$IF -n bst0 2>&1" # --- the state record, consulted in the dry run ------------------------------ # Before either tool acts it consults the PER-INTERFACE state file, # /ifstate., whose presence is what "configured" means; the # ifstate summary file is not what that decision reads. ifdown refuses an # interface it has no record of, dry run or not, so the down direction is # asserted against a seeded record in a state dir of its own. That drives the # same path ifdown really takes -- as opposed to --force, which reaches the down # commands by skipping the state check, and so would assert the stanza # conversion while dropping the consult that guards it. # # lock_interface() tolerates a failed advisory lock under -n (main.c: its err() # arms are `if (!no_act)`), which is why these three assert on a target where the # real transitions below cannot -- see the BRINTOS-WORKAROUND note there. seed="$tmp/seeded" mkdir -p "$seed" printf 'bst0\n' > "$seed/ifstate.bst0" SIF="ifup -i $cfg --state-dir $seed --no-scripts" SID="ifdown -i $cfg --state-dir $seed --no-scripts" # The static `down` sequence flushes and downs the link. check_out ifupdown/dryrun-down "ip addr del" sh -c "$SID -n bst0 2>&1" # The refusal is correct behaviour and is asserted, not worked around: with no # record, ifdown declines rather than emitting a down sequence for an interface # it never brought up. check_out ifupdown/down-unconfigured "not configured" sh -c "$ID -n bst0 2>&1" # ifup reads the same record and declines the redo. check_out ifupdown/dryrun-already "already configured" sh -c "$SIF -n bst0 2>&1" # An interface with no stanza is an error, not a silent success. check_rc ifupdown/unknown-iface 1 sh -c "$IF -n nosuchif0 >/dev/null 2>&1" # A malformed stanza must fail the parse rather than be skipped. bad="$tmp/interfaces.bad" printf 'iface bst9 inet static\n\taddress\n' > "$bad" check_rc ifupdown/parse-error 1 \ sh -c "ifup -i $bad --state-dir $tmp --no-scripts -n bst9 >/dev/null 2>&1" # --- real state transitions (not a dry run) --------------------------------- # `iface lo inet loopback` is the one stanza whose up/down commands are guarded # by `if (!iface_is_lo())`, so ifup/ifdown on lo run NO kernel command while still # taking the full real path: fcntl(F_SETLK/F_SETLKW) on the state lock, and the # read/modify/write of ifstate. That makes it a safe on-target exercise of the # machinery the dry run skips. # # BRINTOS-WORKAROUND: brintos/linux#16 remove -- glibc on this 32-bit target # always sends the LFS lock commands (F_GETLK64/F_SETLK64/F_SETLKW64 = 12/13/14) # and sys_fcntl64 is unwired, so every POSIX advisory lock returns EINVAL and ifup # exits before it can write state. The capability is probed, not assumed, and only # that one signature skips: any other failure still fails the case. When #16 # closes the probe stops matching and all five assert unconditionally. probe="$tmp/lockprobe" mkdir -p "$probe" lock_err=$(ifup -i "$cfg" --state-dir "$probe" --no-scripts lo 2>&1) rm -rf "$probe" case $lock_err in *"failed to lock lockfile"*) posix_lock=no ;; *) posix_lock=yes ;; esac if [ "$posix_lock" = yes ]; then check ifupdown/state-up sh -c "$IF lo >/dev/null 2>&1" check_out ifupdown/state-file "lo=lo" sh -c "cat $tmp/ifstate 2>&1" # A second ifup is a no-op that says so (rc is non-zero; the message is the point). check_out ifupdown/state-already "already configured" sh -c "$IF lo 2>&1 || true" check ifupdown/state-down sh -c "$ID lo >/dev/null 2>&1" check ifupdown/state-cleared sh -c "! grep -q '^lo=' $tmp/ifstate 2>/dev/null" else for c in state-up state-file state-already state-down state-cleared; do bst_skip "ifupdown/$c" \ "POSIX advisory locking returns EINVAL, so ifup cannot take its ifstate lock and no real transition can run -- brintos/linux#16" done fi # --- dummy interface, up then down ------------------------------------------ # The ticket's deliverable: bring a real interface up from the interfaces file # and back down, checking the address lands and is withdrawn. Needs `ip` and a # kernel that can create a dummy link; both are probed, and a kernel without # dummy support is reported as skip (a coverage gap to close) rather than a # failure of ifupdown. if ! command -v ip >/dev/null 2>&1; then bst_skip ifupdown/dummy-up "iproute2 ip not installed on this image" bst_skip ifupdown/dummy-down "iproute2 ip not installed on this image" elif ! ip link add bst0 type dummy >/dev/null 2>&1; then bst_skip ifupdown/dummy-up "kernel cannot create a dummy link (no CONFIG_DUMMY)" bst_skip ifupdown/dummy-down "kernel cannot create a dummy link (no CONFIG_DUMMY)" else check_out ifupdown/dummy-up "10.99.0.1" \ sh -c "$IF bst0 >/dev/null 2>&1; ip -4 addr show dev bst0 2>&1" check ifupdown/dummy-down \ sh -c "$ID bst0 >/dev/null 2>&1; ! ip -4 addr show dev bst0 2>/dev/null | grep -q 10.99.0.1" ip link del bst0 >/dev/null 2>&1 fi rm -rf "$tmp"