. "$BST_LIB" # BST-TIMEOUT: 900 # dhcp-relay 4.4.3-P1 -- ISC's DHCP relay agent, dhcrelay, built alone out of # the ISC DHCP tree (meta-openembedded/meta-networking, whose 0001 patch drops # every other Makefile.am target). Test-plan entry BRINTOS-2077. # # A relay agent sits between a client broadcast domain and a DHCP server on # another one. THIS MACHINE HAS NEITHER, and that is the subject rather than an # obstacle: what is asserted here is that dhcrelay says what it cannot do, with # the status it documents, and exits -- and that everything it can decide # WITHOUT a wire it decides correctly. # # --------------------------------------------------------------------------- # THREE WALLS, IN THE ORDER main() MEETS THEM. Getting them the wrong way round # is what makes a case assert the wrong thing, so they are named up front. # # 1. THE PRE-PASS. main() scans argv for -d, --version and --help/-h BEFORE it # does anything else, and answers the last two with exit(0) on the spot. # Those two cases therefore need no -d and touch nothing. # # 2. THE FORK. Without -d, dhcrelay forks a daemon BEFORE parsing its options: # the parent blocks reading a byte from a pipe the child writes only once it # has reached the daemon block. Every usage-error case below passes -d, not # for tidiness but because otherwise it is the CHILD that reports and the # parent's status that is scored. One case deliberately does not, to # exercise the handshake itself. # # 3. THE PACKET SOCKET. configure selects the LPF arm here (config.h defines # HAVE_LPF, and includes/osdep.h turns that into USE_LPF_SEND/RECEIVE), so # the FIRST thing discover_interfaces() does for a requested interface is # socket(PF_PACKET, SOCK_RAW, ETH_P_ALL) in common/lpf.c:83. This kernel is # built `# CONFIG_PACKET is not set' (arch/wasm32/configs/wasm32_defconfig: # "raw sockets -- no consumer on the v0.1 surface"), the same absence # 88-dhcpcd.test records from the client side. So every run that names an # interface is expected to stop there. # # --------------------------------------------------------------------------- # THE TICKET'S SECOND ASSERTION IS NOT WHAT THE SOURCE DOES, and the suite pins # the real answer rather than the expected one. `dhcrelay -i ' does # NOT report ": not found": common/discover.c:775 reaches that log_fatal # only inside `if (!(tmp->flags & INTERFACE_REQUESTED))', which cannot hold for # an interface the command line requested -- on a DHCPv6 build, which this is # (configure reports `dhcpv6: yes'). The weeding branch is skipped and the run # falls through to the packet socket of wall 3, which fails identically whether # the interface exists or not. Both cases are scored here, and `absent-if-same- # wall' asserts they agree, so the finding is recorded rather than hidden. # # --------------------------------------------------------------------------- # TWO MORE ASSERTIONS THE SOURCE DOES NOT HONOUR, kept as green cases stating # what it really does: # # -c relay/dhcrelay.c:429 is `hcount = atoi(argv[i]); if (hcount <= # 255) max_hop_count = hcount;'. There is NO lower bound and no # numeric check, so -c -1 and -c brintos are both ACCEPTED (atoi # gives -1 and 0). Only > 255 is rejected. # -A line 496 rejects only `> DHCP_MTU_MAX' (1500, includes/dhcp.h: # 41). -A 0 is accepted. # # A suite that asserted the documented intent would red on a correct build of # this package, which is the failure mode this file is written to avoid. # # --------------------------------------------------------------------------- # WHAT IS *NOT* RUN, AND WHY. `dhcrelay ' with no -d and no -i would # fork, and the child would reach dispatch() and stay resident for the rest of # the boot -- discover_interfaces() SKIPS loopback and non-broadcast interfaces # (common/discover.c:629-637), so nothing is registered, nothing fails, and # maybe_setup_fallback() leaves it with a UDP socket and an empty event loop. # A stray daemon would outlive this suite and every suite after it. The same # shape IS exercised, bounded and in the foreground, by `resident-without-if' # below, which this suite kills at its own bound. # # Every invocation is bounded by dr_run's /proc/uptime loop rather than by # timeout(1), which can kill nothing dr_run cannot (brintos/linux#21) and whose # timer never fired before the brintos/linux#77 fix. require dhcrelay dhcp-relay/present DR=/usr/sbin/dhcrelay W=${TMPDIR:-/tmp}/bst-dhcp-relay.$$ rm -rf "$W"; mkdir -p "$W" || { bst_notok dhcp-relay/workdir "cannot create $W"; exit 0; } BOUND=45 # per invocation. Every case here either reports and exits # at once or does not intend to return at all, so this is a # hang detector rather than a work budget. # dr_run -- run bounded, with stdout at $W/out and stderr at # $W/err and no result line of its own. DR_RC is the exit status, or 124 when # the bound expired and the child was killed. DR_TEXT is both streams joined, # DR_STDOUT stdout alone. Copied from bst_probe's shape for the reasons its own # header gives: a completion sentinel rather than `kill -0' (a fast child's # zombie reads as alive forever), /proc/uptime as the clock, and nothing but # builtins beside the `sleep 1' in the loop body. DR_RC=0 DR_TEXT= DR_STDOUT= dr_run() { _dr_bound=$1; shift rm -f "$W/out" "$W/err" "$W/rc" { "$@" >"$W/out" 2>"$W/err"; echo $? >"$W/rc"; } & _dr_pid=$! _dr_t0= if [ -r /proc/uptime ] && read -r _dr_u _ < /proc/uptime; then _dr_t0=${_dr_u%.*}; fi _dr_ticks=0 DR_RC=124 while [ ! -e "$W/rc" ]; do _dr_d=$_dr_ticks if [ -n "$_dr_t0" ] && read -r _dr_u _ < /proc/uptime; then _dr_d=$(( ${_dr_u%.*} - _dr_t0 )) fi if [ "$_dr_d" -ge "$_dr_bound" ]; then kill -9 "$_dr_pid" 2>/dev/null wait "$_dr_pid" 2>/dev/null DR_RC=124 break fi sleep 1 _dr_ticks=$((_dr_ticks + 1)) done if [ -e "$W/rc" ]; then DR_RC=0 # $W is under $TMPDIR, so this redirect is on tmpfs and cannot take the # dup2->513 path brintos/linux#73 describes for the virtiofs root. [ -s "$W/rc" ] && read -r DR_RC < "$W/rc" 2>/dev/null case "$DR_RC" in ''|*[!0-9]*) DR_RC=0 ;; esac fi DR_STDOUT=$(cat "$W/out" 2>/dev/null) DR_TEXT=$(cat "$W/out" "$W/err" 2>/dev/null) } # dr_has / dr_hasnt -- score the run dr_run just did. dr_has() { case "$DR_TEXT" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want:'$2' got:'$(_bst_1line "$DR_TEXT")'" ;; esac } dr_hasnt() { case "$DR_TEXT" in *"$2"*) bst_notok "$1" "did not expect:'$2' in '$(_bst_1line "$DR_TEXT")'" ;; *) bst_ok "$1" ;; esac } # dr_usage_error -- the whole shape of one usage() refusal in # one call: bounded, exit 1, and the specific message present. usage() prints # the four banner lines, then the specific error (site.h:319 defines # PRINT_SPECIFIC_CL_ERRORS), then log_fatal(DHCRELAY_USAGE) -- so the usage text # is asserted too, which is what separates usage() from a bare log_fatal. dr_usage_error() { _du_id=$1; _du_want=$2; shift 2 dr_run "$BOUND" "$@" check_eq "dhcp-relay/$_du_id-rc" "1" "$DR_RC" dr_has "dhcp-relay/$_du_id" "$_du_want" dr_has "dhcp-relay/$_du_id-usage" "Usage: dhcrelay" } # dr_accepted -- the option parsed CLEANLY: the run got all the # way to the "No servers specified." check, which sits after the whole option # loop and before discover_interfaces(). Asserting acceptance positively like # this is what keeps it from passing on a run that died even earlier. dr_accepted() { _da_id=$1; shift dr_run "$BOUND" "$@" check_eq "dhcp-relay/$_da_id-rc" "1" "$DR_RC" dr_has "dhcp-relay/$_da_id" "No servers specified." } # ------------------------------------------------------------- inventory -- check_eq dhcp-relay/inventory "yes" "$([ -x "$DR" ] && echo yes)" # The bare name a user types must be this binary; `command -v' reads root's # actual PATH, so this also catches an image on which /usr/sbin lost its place. check_eq dhcp-relay/on-path "$DR" "$(command -v dhcrelay 2>/dev/null)" # The 0001 patch reduces Makefile.am to the relay alone. If the server or the # client ever appeared they would be unported binaries nobody asserted, so # their absence is pinned rather than assumed. check_eq dhcp-relay/relay-only "" "$(ls /usr/sbin/dhcpd /usr/sbin/dhclient /usr/bin/omshell 2>/dev/null)" # CONFFILES:${PN}. The init script refuses to start without this file, so a red # here separates "not shipped" from "shipped and wrong". check dhcp-relay/default-shipped test -f /etc/default/dhcp-relay for k in SERVERS INTERFACES OPTIONS; do check "dhcp-relay/default-key-$k" grep -q "^$k=" /etc/default/dhcp-relay done # "systemd" is not in DISTRO_FEATURES, so systemd.bbclass packages nothing and # do_install's `else' arm installs the sysvinit script instead. Both halves are # asserted: the script present, and dhcrelay's OWN unit absent. Asserted against # the unit name rather than against /lib/systemd existing, because other # packages on this image ship units into that directory and the wider assertion # would red on a file dhcp-relay never wrote. check dhcp-relay/initscript-shipped test -x /etc/init.d/dhcp-relay check_eq dhcp-relay/no-systemd-unit "" \ "$(ls -d /lib/systemd/system/dhcrelay* /usr/lib/systemd/system/dhcrelay* 2>/dev/null)" # The init script and includes/dhcpd.h:1611 must agree about where the pid file # goes -- LOCALSTATEDIR is compiled in as "/var", so both say /var/run/ # dhcrelay.pid. A disagreement means `stop' would not find a running daemon. check dhcp-relay/initscript-pidpath grep -q '^DHCRELAYPID=/var/run/dhcrelay.pid$' /etc/init.d/dhcp-relay # -doc IS INSTALLED AND -dev / -staticdev ARE NOT, and that is not a matter of # taste: the recipe declares RCONFLICTS:${PN}-dev = "bind-dev", because both # packages carry the bundled bind's headers and archives under the same paths -- # and this image already installs `bind'. So the four man pages ship and the # headers do not. Read back from the built .deb, those four are ISC DHCP's OWN -- # dhcrelay.8, omapi.3, dhcp-eval.5, dhcp-options.5 -- and none of bind's, which # is why -doc needs no RCONFLICTS against bind-doc the way -dev needs one against # bind-dev. Both halves are pinned: a -dev that appeared here would be a # package-manager conflict on the next image that also wanted bind-dev. check dhcp-relay/manpage-shipped test -f /usr/share/man/man8/dhcrelay.8 # Asserted over libdhcp.a and libomapi.a specifically -- dhcp's OWN archives, # which no other package on this image ships -- rather than over the bind ones # they sit beside, so a red here names dhcp-relay-staticdev and cannot be # triggered by somebody else adding bind-staticdev later. check_eq dhcp-relay/no-staticdev "" "$(ls /usr/lib/libdhcp.a /usr/lib/libomapi.a 2>/dev/null)" check_eq dhcp-relay/no-dev "" "$(ls /usr/include/dhcpctl/dhcpctl.h /usr/include/omapip/omapip.h 2>/dev/null)" # dhcp-relay converts under BRINTOS_PACKAGE_SHARED, so dhcp's own libdhcp and # libomapi ship as shared objects beside the bundled bind's. Presence is asserted # on dhcp's two alone, which no other package ships; the shape is asserted on # all six, so a dangling link or an empty file reads red. check dhcp-relay/libdhcp-shared sh -c 'set -- /usr/lib/libdhcp.so.*; [ -e "$1" ]' check dhcp-relay/libomapi-shared sh -c 'set -- /usr/lib/libomapi.so.*; [ -e "$1" ]' bst_lib_shape dhcp-relay/lib-shape \ '/usr/lib/libisc*.so*' '/usr/lib/libdns*.so*' '/usr/lib/libisccfg*.so*' \ '/usr/lib/libirs*.so*' '/usr/lib/libdhcp*.so*' '/usr/lib/libomapi*.so*' # ------------------------------------------------- the pre-pass, wall 1 -- # --version and --help are answered inside main()'s first loop, ahead of the # fork, the option parser and every socket. A red in this block separates "the # binary is broken" from every case below it. dr_run "$BOUND" "$DR" --version check_eq dhcp-relay/version-rc "0" "$DR_RC" dr_has dhcp-relay/version "isc-dhcrelay-4.4.3-P1" # log_info() writes through errwarn.c's log_perror arm, which is write(2) to # STDERR_FILENO. A port whose diagnostics moved to stdout would still "work" # while breaking every consumer that reads dhcrelay the documented way. check_eq dhcp-relay/version-on-stderr "" "$DR_STDOUT" dr_run "$BOUND" "$DR" -h check_eq dhcp-relay/help-rc "0" "$DR_RC" dr_has dhcp-relay/help-usage "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]" DR_HELP_SHORT=$DR_TEXT # The -6 synopsis is the DHCPv6 half of DHCRELAY_USAGE and is compiled in only # when DHCPv6 is defined -- which is what makes the `Command not used for # DHCPv6' cases below reachable at all, so it is asserted rather than assumed. dr_has dhcp-relay/help-v6-synopsis "dhcrelay -6 [-d] [-q] [-I] [-c ]" # RELAY_PORT is off (configure reports `relay-port: no'), so -rp is not in the # synopsis. This pins the build's feature set from the artifact rather than from # the configure log. dr_hasnt dhcp-relay/help-no-relay-port "-rp " # usage() substitutes isc_file_basename(progname), so an absolute argv[0] must # still print the bare name. dr_hasnt dhcp-relay/help-basename "Usage: /usr/sbin/dhcrelay" dr_run "$BOUND" "$DR" --help check_eq dhcp-relay/help-long-rc "0" "$DR_RC" check_eq dhcp-relay/help-long-same "$DR_HELP_SHORT" "$DR_TEXT" # ------------------------------------ the option parser, all behind -d -- # The ticket's first assertion: no upstream server is the documented usage # error. It is a bare log_fatal (relay/dhcrelay.c:730), NOT usage(), so the # banner and the synopsis are absent -- which is asserted, because scoring it # with dr_usage_error would have been wrong in a way nothing else would catch. dr_run "$BOUND" "$DR" -d check_eq dhcp-relay/no-servers-rc "1" "$DR_RC" dr_has dhcp-relay/no-servers "No servers specified." dr_hasnt dhcp-relay/no-servers-not-usage "Usage: dhcrelay" # log_fatal's own tail, six lines of bug-reporting advice and then "exiting.". # Asserting it is how a genuine fatal is told apart from a message printed on # the way to somewhere else. dr_has dhcp-relay/no-servers-exiting "exiting." dr_usage_error unknown-opt "Unknown command: -Z" "$DR" -d -Z # usage() prints the four banner lines before the specific error; the version in # them comes from PACKAGE_VERSION, the same string --version prints. dr_has dhcp-relay/unknown-opt-banner "Internet Systems Consortium DHCP Relay Agent 4.4.3-P1" dr_usage_error noarg-c "No argument for command: -c" "$DR" -d -c dr_usage_error noarg-i "No argument for command: -i" "$DR" -d -i dr_usage_error noarg-pf "No argument for command: -pf" "$DR" -d -pf dr_usage_error noarg-A "No argument for command: -A" "$DR" -d -A dr_usage_error noarg-m "No argument for command: -m" "$DR" -d -m dr_usage_error noarg-U "No argument for command: -U" "$DR" -d -U # ------------------------------------------------------- -c, the hop count -- dr_usage_error hops-over "Bad hop count to -c: 256" "$DR" -d -c 256 # 255 is the last accepted value -- the comparison is `<= 255'. The boundary is # what a regression moves first. dr_accepted hops-max "$DR" -d -c 255 # NO LOWER BOUND AND NO NUMERIC CHECK. atoi("-1") is -1 and atoi("brintos") is # 0, both <= 255, so both are taken. Green here means the source behaves as # read; a red means upstream added the validation this ticket assumed it had. dr_accepted hops-negative "$DR" -d -c -1 dr_accepted hops-nonnumeric "$DR" -d -c brintos # --------------------------------------- -A, the agent-option packet length -- dr_run "$BOUND" "$DR" -d -A 65535 check_eq dhcp-relay/maxlen-over-rc "1" "$DR_RC" dr_has dhcp-relay/maxlen-over "packet length exceeds" # Also a bare log_fatal rather than usage(), for the same reason as -c above but # a different code path, so it is pinned separately. dr_hasnt dhcp-relay/maxlen-over-not-usage "Usage: dhcrelay" dr_accepted maxlen-max "$DR" -d -A 1500 # DHCP_MTU_MAX, includes/dhcp.h:41 dr_accepted maxlen-zero "$DR" -d -A 0 # no lower bound # ---------------------------------------------- -m, the agent-option policy -- dr_usage_error mode-unknown "Unknown argument to -m: brintos" "$DR" -d -m brintos for m in append replace forward discard; do dr_accepted "mode-$m" "$DR" -d -m "$m" done # The comparison is strcasecmp, so the spelling is case-insensitive. dr_accepted mode-uppercase "$DR" -d -m APPEND # ----------------------------------------------------- -g, the giaddr -- dr_usage_error gateway-bad "Invalid gateway address 'brintos'" "$DR" -d -g brintos dr_accepted gateway-good "$DR" -d -g 10.77.0.1 # --------------------------------------------- -i / -iu / -id, the name -- # request_v4_interface() (relay/dhcrelay.c:2156) refuses `len >= sizeof(tmp-> # name)', and that field is char[IFNAMSIZ] with IFNAMSIZ 16 -- so 16 characters # is already too long and 15 is the last accepted length. The message names the # measured length, which is the field a bounds regression moves. LONGIF=abcdefghijklmnop # 16 OKIF=abcdefghijklmno # 15 for f in i iu id; do dr_run "$BOUND" "$DR" -d "-$f" "$LONGIF" check_eq "dhcp-relay/ifname-long-$f-rc" "1" "$DR_RC" dr_has "dhcp-relay/ifname-long-$f" "$LONGIF: interface name too long (is 16)" done # It is a log_fatal, not usage() -- the one refusal in the -i family that prints # no synopsis. Asserted once, on the -i arm. dr_run "$BOUND" "$DR" -d -i "$LONGIF" dr_hasnt dhcp-relay/ifname-long-not-usage "Usage: dhcrelay" # 15 characters is accepted, and the run reaches the server check -- which also # says the name was never looked up on the way past, since no such interface # exists. dr_accepted ifname-15 "$DR" -d -i "$OKIF" # ----------------------------------------------- the two protocol families -- # These are reachable only because DHCPv6 is compiled in; help-v6-synopsis above # is what says so independently. dr_usage_error v6-rejects-v4-opt "Command not used for DHCPv6: -a" "$DR" -d -6 -a dr_usage_error v4-rejects-v6-opt "Command not used for DHCPv4: -l" "$DR" -d -4 -l lo dr_usage_error proto-already-set "Protocol already set, -6 inappropriate" "$DR" -d -4 -6 # ------------------------------------------------------- name resolution -- # A server argument that is neither an address nor a resolvable name is # log_error, NOT log_fatal (relay/dhcrelay.c:643) -- the run continues, collects # no server, and stops at the check further down. Both halves are asserted, # because "it failed" alone would also pass if the resolver failure had been # made fatal, which would be a behaviour change nobody would see. dr_run "$BOUND" "$DR" -d no-such-host.brintos.invalid check_eq dhcp-relay/host-unknown-rc "1" "$DR_RC" dr_has dhcp-relay/host-unknown "no-such-host.brintos.invalid: host unknown" dr_has dhcp-relay/host-unknown-continues "No servers specified." # ------------------------------------------------ wall 3, the packet socket -- # THE TICKET'S THIRD ASSERTION. A literal address IS accepted as a server -- it # goes through inet_aton, not the resolver -- so this run gets past the check # every case above stopped at, and reaches discover_interfaces(). That is the # only way to prove the address was taken, and it is what puts the run against # the packet socket. # # Two spellings of the failure are documented, chosen by errno: common/lpf.c:85 # tests a set of "this family is not supported" values and prints the # CONFIG_PACKET advice, and anything else falls through to "Open a socket for # LPF: %m". Either is a correct report of the same absence; which one arrives # is a fact about the kernel's errno, not about this port, so the branch is # recorded and then asserted to be CONSISTENT rather than pinned to a guess. DR_WALL= dr_wall() { _dw_id=$1 case "$DR_TEXT" in *"CONFIG_PACKET (Packet socket) and CONFIG_FILTER"*) _dw_b=config-packet ;; *"Open a socket for LPF:"*) _dw_b=lpf-socket ;; *"Failed to get interface index:"*) _dw_b=ifindex ;; *"Bind socket to interface:"*) _dw_b=lpf-bind ;; *) bst_notok "$_dw_id" "no documented packet-socket failure in '$(_bst_1line "$DR_TEXT")'" return 1 ;; esac [ -n "$DR_WALL" ] || DR_WALL=$_dw_b bst_ok "$_dw_id" return 0 } dr_run "$BOUND" "$DR" -d -i lo 10.77.0.1 # Terminating is the assertion. A relay that stayed resident on a machine with # no packet socket is the failure this ticket exists to exclude. check_eq dhcp-relay/lo-terminates "no" "$([ "$DR_RC" = 124 ] && echo yes || echo no)" check_eq dhcp-relay/lo-rc "1" "$DR_RC" dr_wall dhcp-relay/lo-wall # It got past "No servers specified.", which is what says 10.77.0.1 was accepted # as a server rather than the run having stopped earlier for an unrelated reason. dr_hasnt dhcp-relay/lo-server-accepted "No servers specified." # THE SAME WALL FOR AN INTERFACE THAT DOES NOT EXIST -- see the header. This is # where the ticket expected ": not found"; on a DHCPv6 build that branch is # unreachable for a requested interface, and the run stops at the packet socket # exactly as the loopback one did. dr_run "$BOUND" "$DR" -d -i brintos0 10.77.0.1 check_eq dhcp-relay/absent-if-terminates "no" "$([ "$DR_RC" = 124 ] && echo yes || echo no)" check_eq dhcp-relay/absent-if-rc "1" "$DR_RC" DR_FIRST_WALL=$DR_WALL dr_wall dhcp-relay/absent-if-wall check_eq dhcp-relay/absent-if-same-wall "$DR_FIRST_WALL" "$DR_WALL" dr_hasnt dhcp-relay/absent-if-not-found "brintos0: not found" # --------------------------------------------------------- -q, the quiet arm -- # -q does two things -- it skips the four banner lines and sets log_perror = 0 # -- and the ORDER is the whole of what this arm asserts. BOTH happen at # relay/dhcrelay.c:687-693, AFTER the argv loop has finished. So -q silences # everything from 693 onward, which is where the banner and the packet socket # of wall 3 both live, and NOTHING the loop itself already logged: `-i' is one # of the options that logs, line 448 calling request_v4_interface(), whose # log_debug at line 2166 announces the request before -q has taken effect. # # The run is therefore not silent, it is one line long. That single line is # asserted whole rather than as an emptiness, so a red says which half moved: # the exact match is also what pins the fatal as silenced, since the wall would # otherwise print here exactly as it does in the -i cases above. # # This is measured, not read off the source -- BRINTOS-2077's phase-A entry # predicted a wholly silent run, and batch-2048's verify produced the request # line. It matters for the init script, whose start-stop-daemon line passes -q: # with no syslogd on this image the fatal is lost outright, while this one line # still reaches whatever holds the daemon's stderr. dr_run "$BOUND" "$DR" -d -q -i lo 10.77.0.1 check_eq dhcp-relay/quiet-rc "1" "$DR_RC" dr_hasnt dhcp-relay/quiet-no-banner \ "Internet Systems Consortium DHCP Relay Agent 4.4.3-P1" check_eq dhcp-relay/quiet-only-request \ "Requesting: lo as upstream: Y downstream: Y" "$DR_TEXT" # ------------------------------------------------------------ the pid file -- # The pid file is written inside `if (!no_daemon)', so a foreground run writes # none -- neither the compiled-in default nor a path given with -pf. Asserted # over a path this test owns as well as the real one, so a red says which. check_eq dhcp-relay/no-default-pidfile "" "$(ls /var/run/dhcrelay.pid 2>/dev/null)" dr_run "$BOUND" "$DR" -d -pf "$W/relay.pid" -i lo 10.77.0.1 check_eq dhcp-relay/pf-rc "1" "$DR_RC" check_eq dhcp-relay/pf-not-written "" "$(ls "$W/relay.pid" 2>/dev/null)" # --no-pid is accepted as an option in its own right. dr_accepted no-pid "$DR" -d --no-pid # ---------------------------------------------------- wall 2, the fork -- # The one case that does NOT pass -d. The parent forks, blocks on the pipe, and # the child dies at the packet socket without ever writing its byte; the # parent's read returns 0 and it _exit(1)s. So a green here is a fork(2), a # pipe(2) and the handshake between them all working -- and the child's # diagnostic still reaches the shared stderr, because log_perror is not cleared # until after discover_interfaces(). # # It is safe to run only because it names an interface: without one the child # would reach dispatch() and never return. See the header. dr_run "$BOUND" "$DR" -i lo 10.77.0.1 check_eq dhcp-relay/fork-terminates "no" "$([ "$DR_RC" = 124 ] && echo yes || echo no)" check_eq dhcp-relay/fork-rc "1" "$DR_RC" dr_wall dhcp-relay/fork-child-reported # ------------------------------------- what a relay with nothing to do does -- # In the FOREGROUND, so this suite owns the process and kills it at the bound. # With no -i, discover_interfaces() registers nothing at all -- loopback and # non-broadcast interfaces are skipped, and this machine has only loopback -- so # there is no packet socket to fail on, maybe_setup_fallback() gives it a UDP # socket, and dispatch() waits for packets that cannot arrive. STAYING RESIDENT # IS THE CORRECT BEHAVIOUR FOR A DAEMON IN THAT POSITION, and it is asserted # positively: exiting instead would mean one of the two skips above had changed, # which is exactly the regression that would make the non-foreground form of # this command leak a daemon into every later suite. dr_run 30 "$DR" -d 10.77.0.1 check_eq dhcp-relay/resident-without-if "yes" "$([ "$DR_RC" = 124 ] && echo yes || echo no)" rm -rf "$W"