. "$BST_LIB" # BST-TIMEOUT: 420 # ptpd 2.3.1 -- the PTPd IEEE 1588 Precision Time Protocol daemon # (meta-networking recipes-daemons/ptpd). What ships is one program, # /usr/bin/ptpd2, its man page, /etc/default/ptpd, and upstream's two config # templates under /usr/share/ptpd (this port's addition -- meta-networking # installs neither, and gates /etc/default/ptpd behind a systemd # DISTRO_FEATURE this distro does not set). # # THE SUBJECT IS THE CONFIGURATION FRONT END, NOT A SYNCHRONISED CLOCK, and # that is a statement about this target rather than a lowered bar. PTP needs a # peer, a multicast group and a timestamping clock; this image has one virtio # interface, no second PTP node behind it and no PHC. Nothing here can converge # an offset, so nothing below claims to. # # WHAT IS REACHABLE IS ptpd2 -k, AND IT IS REACHABLE IN FULL. ptpdStartup() # runs the whole of its front end -- getopt_long, the config file, parseConfig's # range/enum/boolean/required checks, and testInterface() -- BEFORE the # `configcheck:' label where -k prints its verdict and returns # (src/dep/startup.c:699-815). So one bounded, socket-free, lock-free command # exercises every validator the daemon has, and its verdict is a whole-program # answer rather than a parser unit test. Each case below asserts the SPECIFIC # diagnostic the checked line produces, not merely a non-zero status: a check # can only fire if the key it names was read, so the message is the evidence # that this exact file reached the parser. # # testInterface() IS INSIDE THAT PATH, so -k also answers whether getifaddrs(2) # resolves interfaces on this kernel -- interfaceExists() is a getifaddrs() walk # (src/dep/net.c). It is asserted as a PAIR, `lo' accepted and a name that does # not exist refused, because either half alone is green for a getifaddrs() that # returned nothing and for one that returned everything. # # THE CONFIGS ARE GENERATED FROM THE BINARY, NOT TYPED HERE. `ptpd2 -O' dumps # the daemon's own defaults, so the base every case derives from is by # construction the version under test rather than a copy of some other # release's, and a default that moves cannot make a case silently assert a # stale value. Overrides are APPENDED rather than sed'd in: ptpd's iniparser # lowercases each key and lets a later entry overwrite an earlier one # (src/dep/iniparser/iniparser.c:376,709), so appending is exact where a # pattern match on `key = value' spacing would be fragile. # # NOTHING IS KILLED AND NOTHING IS WAITED ON. kill(2) reports success and # delivers nothing on this image, SIGKILL included (brintos/linux#21), so the # one live ptpd2 started at the end outlives the suite and one run per boot is # the supported shape. ptpd2 would not exit anyway: protocol() treats a failed # doInit() as `retry in N seconds' and loops forever (src/protocol.c:225-245), # so there is no exit status to read and the criterion is what it WROTE. # Readiness is polled for out of the daemon's own status file, never taken from # a sleep -- relative timers are broken here (brintos/linux-in-the-browser#120: # nanosleep returns early, poll timeouts do not fire), so a `sleep 2' proves # nothing about how much time passed. require /usr/bin/ptpd2 ptpd/present P=/usr/bin/ptpd2 DEFARGS=/etc/default/ptpd TMPL=/usr/share/ptpd/ptpd2.conf.minimal TMPLFULL=/usr/share/ptpd/ptpd2.conf.default-full D=${TMPDIR:-/tmp}/bst-ptpd.$$ rm -rf "$D"; mkdir -p "$D" || { bst_notok ptpd/workdir "cannot create $D"; exit 0; } # has ... : true iff EVERY needle is a substring. has() { _h=$1; shift for _n in "$@"; do case "$_h" in *"$_n"*) ;; *) return 1 ;; esac; done return 0 } # say : ok iff printed , WHATEVER its exit # status. Every refusal below is scored this way on purpose: a correct refusal # exits 1, so check_out -- which also requires rc=0 -- would turn each one red. # The status is asserted separately, and only where it is itself documented. say() { _id=$1; _needle=$2; shift 2 _o=$(bst_bound 60 "$@" 2>&1) if bst_execfail "$_o"; then bst_unrun "$_id" "$_o" elif has "$_o" "$_needle"; then bst_ok "$_id" else bst_notok "$_id" "want:'$_needle' got:'$(_bst_1line "$_o")'"; fi } # --- the program is present and is 2.3.1 ------------------------------------- # -v returns before any config work, so this separates "the recipe built 2.3.1" # from "something else answers to ptpd2". # BST-TIMEOUT-OK: ptpd refusals: each exits non-zero on a config it rejects. The one # invocation that could daemonise goes through say(), which is bounded properly. check_out ptpd/version "ptpd2 version 2.3.1" timeout 60 "$P" -v # --- the help surface -------------------------------------------------------- # Three separate generators, and each is asserted on content only it produces: # printShortHelp() lists the short options, printLongHelp() adds the preset # table and the settings, and printSettingHelp() answers for one key. check_out ptpd/short-help "-i --interface" timeout 60 "$P" -h check_out ptpd/long-help "AVAILABLE PTP ENGINE PRESETS" timeout 60 "$P" -H check_out ptpd/long-help-covers-settings "ptpengine:domain" timeout 60 "$P" -H # argc==1 takes the same short_help label but sets *ret=1 first # (src/dep/daemonconfig.c:2760). Both halves are asserted: the text is the # help, and the status is the one thing that distinguishes it from `-h'. noargs=$(timeout 60 "$P" 2>&1); noargs_rc=$? if bst_execfail "$noargs"; then bst_unrun ptpd/no-args-is-help "$noargs" elif has "$noargs" "-i --interface" && [ "$noargs_rc" -eq 1 ]; then bst_ok ptpd/no-args-is-help else bst_notok ptpd/no-args-is-help "rc=$noargs_rc want rc=1 + short help, got:'$(_bst_1line "$noargs")'"; fi say ptpd/setting-help "ptpengine:domain" "$P" -e ptpengine:domain say ptpd/setting-help-unknown "Unknown setting: nosuch:key" "$P" -e nosuch:key say ptpd/bad-option "Run ptpd2 with -h to see available command-line options." "$P" -Z # --- the base config comes out of the binary --------------------------------- "$P" -O > "$D/base.conf" 2>"$D/base.err" base=$(cat "$D/base.conf" 2>/dev/null) if has "$base" "ptpengine:interface" "ptpengine:preset" "ptpengine:domain"; then bst_ok ptpd/default-config-dump else bst_notok ptpd/default-config-dump "-O did not dump a config -- $(_bst_1line "$base$(cat "$D/base.err" 2>/dev/null)")" fi # conf ... : base.conf plus overrides, later wins. conf() { _n=$1; shift cp "$D/base.conf" "$D/$_n" 2>/dev/null || return 1 for _l in "$@"; do printf '%s\n' "$_l" >> "$D/$_n"; done printf '%s' "$D/$_n" } # --- the criterion: a config the daemon accepts, whole ---------------------- # `lo' is the interface, and it is the right one for this assertion rather than # a convenience: the default transport is UDP_IPV4, for which testInterface() # requires only an IPv4 address (src/dep/net.c:452-470), and lo has 127.0.0.1 # on any kernel that brings it up. The virtio interface is probed separately # below, where its absence is a skip rather than a red. OKCONF=$(conf ok.conf 'ptpengine:interface = lo') say ptpd/config-ok "Configuration OK" "$P" -k -c "$OKCONF" # ...and its exit status, which is the documented one for a clean check. check_rc ptpd/config-ok-exit-code 0 timeout 60 "$P" -k -c "$OKCONF" # --- each validator, asserted by the message only it emits ------------------- # The four families are separate code in parseConfig: the required-key check # (CONFIG_KEY_REQUIRED), the integer range check (CONFIG_KEY_RANGECHECK_INT), # the enumerated-value check (CONFIG_MAP_SELECTVALUE), and the boolean check # (CONFIG_MAP_BOOLEAN) -- daemonconfig.c:152, 257, 725, 386 respectively. One # case each, so a change that breaks one is not masked by the others. say ptpd/config-interface-required \ 'Configuration error: option "ptpengine:interface" is required' \ "$P" -k -c "$(conf noiface.conf 'ptpengine:interface = ')" say ptpd/config-range-checked \ 'Configuration error: option "ptpengine:domain=300" not within allowed range: 0..127' \ "$P" -k -c "$(conf range.conf 'ptpengine:interface = lo' 'ptpengine:domain = 300')" say ptpd/config-enum-checked \ 'Configuration error: option "ptpengine:preset" has unknown value: bogus' \ "$P" -k -c "$(conf enum.conf 'ptpengine:interface = lo' 'ptpengine:preset = bogus')" say ptpd/config-boolean-checked \ 'has unknown boolean value' \ "$P" -k -c "$(conf bool.conf 'ptpengine:interface = lo' 'clock:no_adjust = maybe')" # A rejected config exits 1 and says so in its own words as well as in the # validator's. Asserted once, on the range case, because it is one code path. say ptpd/config-errors-reported "Configuration has errors" \ "$P" -k -c "$D/range.conf" check_rc ptpd/config-error-exit-code 1 timeout 60 "$P" -k -c "$D/range.conf" # An UNKNOWN key is a warning, not an error -- findUnknownSettings() emits # WARNING and parseConfig still succeeds (daemonconfig.c:1143-1155). Both # halves are asserted, because "it warned" and "it still accepted the file" # are different claims and only the pair pins the documented behaviour. The # key is compared lowercased: iniparser lowercases every key it reads. unk=$(timeout 60 "$P" -k -c "$(conf unknown.conf 'ptpengine:interface = lo' 'nosuch:option = 1')" 2>&1) if bst_execfail "$unk"; then bst_unrun ptpd/config-unknown-entry-warned "$unk" elif has "$unk" "Unknown configuration entry: nosuch:option" "Configuration OK"; then bst_ok ptpd/config-unknown-entry-warned else bst_notok ptpd/config-unknown-entry-warned "want the warning AND acceptance, got:'$(_bst_1line "$unk")'" fi # --- a config file that cannot be read -------------------------------------- # loadConfigFile() reports the path it could not open and sets *ret=1 # (daemonconfig.c:2569-2578, startup.c:757). Message and status both. say ptpd/config-unreadable "Could not load configuration file: $D/nosuchfile.conf" \ "$P" -k -c "$D/nosuchfile.conf" check_rc ptpd/config-unreadable-exit-code 1 timeout 60 "$P" -c "$D/nosuchfile.conf" # --- the interface check, as a pair ----------------------------------------- # getInterfaceInfo() names the interface it could not find (net.c:415) and # ptpdStartup() adds its own line (startup.c:794). The positive half is # ptpd/config-ok above; without it a getifaddrs() that resolved NOTHING would # make this case green for the wrong reason. say ptpd/unknown-interface "Interface bst-nosuch0 does not exist." \ "$P" -k -c "$OKCONF" -i bst-nosuch0 say ptpd/unknown-interface-refused "Error: Cannot use bst-nosuch0 interface" \ "$P" -c "$OKCONF" -i bst-nosuch0 check_rc ptpd/unknown-interface-exit-code 1 timeout 60 "$P" -c "$OKCONF" -i bst-nosuch0 # --- the lock file the daemon would take ------------------------------------ # -p prints the resolved lock path and returns 0 without creating anything # (startup.c:781-786), so this reads the auto-lock naming rule -- lock_directory # plus the interface and preset -- on a target where /var/run may not be # writable at all. lk=$(timeout 60 "$P" -p -c "$(conf lock.conf 'ptpengine:interface = lo' "global:lock_directory = $D")" 2>&1) if bst_execfail "$lk"; then bst_unrun ptpd/print-lockfile "$lk" elif has "$lk" "$D/" ".lock"; then bst_ok ptpd/print-lockfile else bst_notok ptpd/print-lockfile "want a .lock path under $D, got:'$(_bst_1line "$lk")'"; fi # --- what the package installs ---------------------------------------------- # /etc/default/ptpd is the argument file meta-networking installs only when # DISTRO_FEATURES carries systemd; this port installs it unconditionally, so a # red here means that bbappend hunk was lost. It is a shell fragment and # nothing on this image sources it -- the assertion is that it is present and # still carries the variable a service would read. if [ -f "$DEFARGS" ]; then da=$(cat "$DEFARGS" 2>&1) if has "$da" "PTPDARGS"; then bst_ok ptpd/default-args-file else bst_notok ptpd/default-args-file "$DEFARGS carries no PTPDARGS -- $(_bst_1line "$da")"; fi else bst_notok ptpd/default-args-file "$DEFARGS is not on the image" fi if [ -f "$TMPL" ]; then bst_ok ptpd/template-minimal-shipped else bst_notok ptpd/template-minimal-shipped "$TMPL is not on the image"; fi if [ -f "$TMPLFULL" ]; then bst_ok ptpd/template-full-shipped else bst_notok ptpd/template-full-shipped "$TMPLFULL is not on the image"; fi # The templates are upstream's own reference input, so the strongest thing that # can be said about them is that the parser under test accepts one. Upstream # ships ptpd2.conf.minimal with `ptpengine:interface=' EMPTY, which ptpd2 # correctly refuses -- so the case fills it in rather than asserting a refusal # that would be about the template's blank rather than about its syntax. if [ -f "$TMPL" ]; then cp "$TMPL" "$D/tmpl.conf" 2>/dev/null printf 'ptpengine:interface = lo\nglobal:log_file = %s/tmpl.log\n' "$D" >> "$D/tmpl.conf" say ptpd/template-parses "Configuration OK" "$P" -k -c "$D/tmpl.conf" else bst_skip ptpd/template-parses "$TMPL is not on the image" fi # The systemd unit is correctly ABSENT and is meant to stay absent: this distro # sets DISTRO_FEATURES without "systemd" and has none to run it. The day that # changes, this case goes red and the port's install rules need re-reading. u=$(ls /lib/systemd/system/ptpd.service /usr/lib/systemd/system/ptpd.service 2>/dev/null | wc -l) if [ "$u" -eq 0 ]; then bst_ok ptpd/no-systemd-unit else bst_notok ptpd/no-systemd-unit "a ptpd.service appeared; DISTRO_FEATURES no longer matches this port's premise"; fi # ptpd-doc holds the ONLY non-empty subpackage -- /usr/share/man/man8/ptpd2.8, # 15 642 bytes -- and EMCRAFT_TOOLS does not name it, which is this image's # convention (25 of 1703 entries are -doc). The man page is therefore expected # ABSENT, and this case is the one that would notice if that changed. if [ ! -e /usr/share/man/man8/ptpd2.8 ]; then bst_ok ptpd/doc-not-installed else bst_notok ptpd/doc-not-installed "ptpd-doc reached the image; EMCRAFT_TOOLS names ptpd alone"; fi # --- there is no PTP hardware clock, and that is established, not assumed ---- # Same probe linuxptp's suite takes. A /dev/ptp* node appearing would mean the # hardware-timestamping premise of this whole port needs re-reading. np=$(ls /dev/ptp* 2>/dev/null | wc -l) if [ "$np" -eq 0 ]; then bst_ok ptpd/no-ptp-device-node else bst_notok ptpd/no-ptp-device-node "this image grew $np /dev/ptp* node(s)"; fi # --- AF_PACKET, read directly, because the pcap arm is scored against it ----- # libpcap's Linux capture backend is AF_PACKET, so `ptpd2 -use_libpcap' can only # work where the kernel carries CONFIG_PACKET. That option is what registers # packet_net_ops and creates /proc/net/packet, so its absence is a reading of # the option rather than an inference from a refusal. 88-mstpd and 79-libnet # take the same probe; the latter measures the syscall itself. packet_proc=$([ -e /proc/net/packet ] && echo present || echo absent) check_eq ptpd/probe-no-af-packet "absent" "$packet_proc" # --- the runtime half, probed ------------------------------------------------ # Everything above is bounded and needs no socket. What follows starts a real # ptpd2 and reports what the platform did with it. It is scored on the daemon's # own artifacts -- the status file it writes at POWERUP before any timer runs # (protocol.c:208-219) and its log -- because ptpd2 never exits: a failed # doInit() is logged as `PTPd init failed - will retry' and the loop continues # (protocol.c:238). There is no exit status to read. # # -C keeps it in the foreground, so the daemon() fork and the second lock check # are skipped; -L skips the lock entirely. Both are needed here rather than # tidy: writeLockFile() targets /var/run, and this image does not provision it. # # `lo' IS THE RIGHT INTERFACE FOR THIS, AND IT WAS CHECKED RATHER THAN ASSUMED. # ptpd2 warns `Interface lo is not multicast capable' and joins the group # anyway; on a native control run this reached PTP_LISTENING with # `Transport: ipv4, multicast'. So a multicast red below is about this kernel, # not about the interface being a loopback. The image's own interface is # exercised separately, at the end. # # EVERY WAIT IS A POLL COUNT, NOT A DURATION. nanosleep(2) returns early here # (brintos/linux-in-the-browser#120), so `sleep 1' is not a second and the # budgets below are deliberately large: a loop that spins is still bounded, and # an over-large bound costs nothing on a run that succeeds. LIVE=$(conf live.conf 'ptpengine:interface = lo' \ "global:log_file = $D/live.log" \ "global:status_file = $D/live.status" \ 'global:log_status = y' \ 'global:foreground = y' \ 'ptpengine:preset = slaveonly' \ 'clock:no_adjust = y') "$P" -C -L -c "$LIVE" > "$D/live.out" 2>&1 & i=0; up=0 while [ $i -lt 300 ]; do [ -s "$D/live.status" ] && { up=1; break; } grep -q 'init failed\|event timers\|Failed to initialize network' "$D/live.out" "$D/live.log" 2>/dev/null && break i=$((i+1)); sleep 1 done live=$(cat "$D/live.out" "$D/live.log" 2>/dev/null) status=$(cat "$D/live.status" 2>/dev/null) # _cause : prefix a red with the platform gap that explains it, so a run # self-attributes instead of leaving a reader to guess which wall was hit. Each # arm names the call and the site, because the same red has more than one cause # and the difference is what a reader needs. _cause() { case "$1" in *"failed to set up event timers"*) printf '%s' "timer_create(2) is unwired (eventtimer_posix.c createEventTimer -> timerSetup FALSE, startup.c *ret=2): " ;; *"failed to join the multicast group"*) printf '%s' "IP_ADD_MEMBERSHIP refused (net.c:527): " ;; *"failed to open event pcap"*) printf '%s' "pcap_open_live() refused (net.c:1018): " ;; *"failed to initialize sockets"*) printf '%s' "socket(PF_INET, SOCK_DGRAM) refused (net.c:961): " ;; *"Failed to allocate memory"*) printf '%s' "the protocol engine could not be allocated (startup.c *ret=2): " ;; esac } # The startup banner is the FIRST criterion, and it is the one that separates # the front end from the network: ptpdStartup() prints it after timerSetup() # has created all sixteen event timers (startup.c:975), so it is the only thing # on this image that says timer_create(2) answered. if has "$live" "started successfully on lo"; then bst_ok ptpd/startup-banner else bst_notok ptpd/startup-banner "$(_cause "$live")no startup banner -- $(_bst_1line "$live")"; fi # The daemon reaching its own status file means protocol() ran: ptpdStartup() # returned a clock and POWERUP was processed. if [ "$up" -eq 1 ]; then bst_ok ptpd/daemon-starts else bst_notok ptpd/daemon-starts "$(_cause "$live")no $D/live.status after ${i} polls -- $(_bst_1line "$live")"; fi # PTP_LISTENING is the criterion for the NETWORK, and it is reached only after # netInit() opened both sockets, resolved the interface, joined the multicast # group and initialised timestamping (protocol.c:655). It is a NOTICE, so it is # in the log without RUNTIME_DEBUG. On a native control run it appeared ~100 ms # after the banner. if has "$live" "Now in state: PTP_LISTENING"; then bst_ok ptpd/port-listening elif has "$live" "Failed to initialize network"; then bst_notok ptpd/port-listening "$(_cause "$live")netInit failed -- $(_bst_1line "$live")" else bst_notok ptpd/port-listening "$(_cause "$live")no PTP_LISTENING after ${i} polls -- $(_bst_1line "$live")" fi # The status file's own content -- the daemon's statement about what it took. # Read from the file rather than from stdout, because it is the artifact a # supervisor would read, and asserted field by field so a truncated file cannot # pass on one lucky substring. if [ -n "$status" ]; then if has "$status" "Interface" "lo"; then bst_ok ptpd/status-file-interface else bst_notok ptpd/status-file-interface "want the interface it took, got:'$(_bst_1line "$status")'"; fi if has "$status" "Port state"; then bst_ok ptpd/status-file-port-state else bst_notok ptpd/status-file-port-state "no port state line -- $(_bst_1line "$status")"; fi # `Transport: ipv4, multicast' is the daemon SAYING which transport it built, # which is a stronger multicast claim than the absence of an error message. if has "$status" "ipv4" "multicast"; then bst_ok ptpd/status-file-transport else bst_notok ptpd/status-file-transport "want an ipv4 multicast transport, got:'$(_bst_1line "$status")'"; fi else bst_notok ptpd/status-file-interface "$(_cause "$live")no status file was written" bst_notok ptpd/status-file-port-state "$(_cause "$live")no status file was written" bst_notok ptpd/status-file-transport "$(_cause "$live")no status file was written" fi # THE MULTICAST JOIN, scored only where the daemon got far enough to attempt it. # netInit() joins 224.0.1.129 and 224.0.0.107 with two IP_ADD_MEMBERSHIP # setsockopts (net.c:527-531); a refusal is PERROR'd and netInit returns FALSE, # which protocol() reports as `Failed to initialize network'. Where startup # never reached netInit there is nothing to say about multicast, and the case # SKIPS on that -- a red there would be about the timers, which # ptpd/startup-banner already owns. if has "$live" "failed to join the multicast group"; then bst_notok ptpd/multicast-join "IP_ADD_MEMBERSHIP refused on lo -- $(_bst_1line "$live")" elif has "$live" "Now in state: PTP_LISTENING"; then bst_ok ptpd/multicast-join elif has "$live" "started successfully on lo"; then bst_notok ptpd/multicast-join "netInit did not complete and did not name the join -- $(_bst_1line "$live")" else bst_skip ptpd/multicast-join "startup did not reach netInit, so the join was never attempted" fi # THE libpcap CAPTURE, taken as its own run because it is its own configuration # (ptpengine:use_libpcap=y) and its failure modes are distinct: pcap_open_live() # on the interface, a pcap_compile of the PTP multicast filter, then # pcap_get_selectable_fd(). The criterion is again the daemon's own words -- # `Transport: ipv4 + libpcap' in the status file, measured on a native control # run -- rather than the absence of an error. if has "$live" "started successfully on lo"; then PCAPC=$(conf pcap.conf 'ptpengine:interface = lo' \ "global:log_file = $D/pcap.log" \ "global:status_file = $D/pcap.status" \ 'global:log_status = y' \ 'global:foreground = y' \ 'ptpengine:use_libpcap = y' \ 'clock:no_adjust = y') "$P" -C -L -c "$PCAPC" > "$D/pcap.out" 2>&1 & j=0 while [ $j -lt 300 ]; do [ -s "$D/pcap.status" ] && break grep -q 'pcap\|Failed to initialize network' "$D/pcap.out" "$D/pcap.log" 2>/dev/null && break j=$((j+1)); sleep 1 done pc=$(cat "$D/pcap.out" "$D/pcap.log" 2>/dev/null) pcs=$(cat "$D/pcap.status" 2>/dev/null) if has "$pc" "failed to open event pcap"; then # The diagnostic line itself, not the head of the log: ptpd's foreground # warning banner prints first and would be all a `_bst_1line $pc' survived # the 100-char truncation. The attribution is appended from the probe above # rather than asserted, so it stays true the day CONFIG_PACKET lands. _pcapline=$(printf '%s\n' "$pc" | grep -m1 'failed to open event pcap') if [ "$packet_proc" = absent ]; then _pcapwhy="this kernel is built '# CONFIG_PACKET is not set', so libpcap's AF_PACKET backend cannot open a capture -- brintos/linux#81 (https://brintos.io/brintos/linux/issues/81); established by ptpd/probe-no-af-packet" else _pcapwhy="this kernel HAS AF_PACKET, so the refusal is not brintos/linux#81 and needs re-reading" fi bst_notok ptpd/pcap-capture "pcap_open_live() refused lo: $_pcapwhy -- $(_bst_1line "$_pcapline")" elif has "$pc" "failed to compile pcap event filter"; then bst_notok ptpd/pcap-capture "pcap_compile() refused the PTP filter -- $(_bst_1line "$pc")" elif has "$pc" "failed to get pcap event fd"; then bst_notok ptpd/pcap-capture "pcap_get_selectable_fd() returned -1 -- $(_bst_1line "$pc")" elif has "$pcs" "libpcap"; then bst_ok ptpd/pcap-capture else bst_notok ptpd/pcap-capture "$(_cause "$pc")no libpcap transport after ${j} polls -- $(_bst_1line "$pc$pcs")" fi else bst_skip ptpd/pcap-capture "the socket run did not start, so a pcap run has nothing to add" fi # SOFTWARE TIMESTAMPING, from the daemon's own diagnostics. netInitTimestamping() # tries SO_TIMESTAMPING, falls back to SO_TIMESTAMPNS and PERRORs whichever it # could not enable (net.c:707-790); a total failure is fatal to netInit, which # PTP_LISTENING therefore rules out. There is no getter for what it settled on, # so the assertion is: the port listened, and neither PERROR was printed. if has "$live" "Now in state: PTP_LISTENING"; then if has "$live" "netInitTimestamping: failed to enable"; then bst_notok ptpd/software-timestamping "a socket timestamping option was refused -- $(_bst_1line "$live")" else bst_ok ptpd/software-timestamping fi else bst_skip ptpd/software-timestamping "netInit did not complete, so netInitTimestamping's result is not this case's to report" fi # THE IMAGE'S OWN INTERFACE, probed rather than named. The config oracle above # uses lo because lo is guaranteed; this case says whether the virtio interface # the image actually has is one ptpd2 would accept, and skips where the image # has none -- which is a statement about the image, not a ptpd failure. IF=$(ls /sys/class/net 2>/dev/null | grep -v '^lo$' | head -1) if [ -n "$IF" ]; then say ptpd/virtio-interface-accepted "Configuration OK" \ "$P" -k -c "$(conf virtio.conf "ptpengine:interface = $IF")" else bst_skip ptpd/virtio-interface-accepted "this image exposes no interface other than lo" fi