. "$BST_LIB" # BST-TIMEOUT: 180 # ~110 cases, ~110 execs, no loops and no corpus. Nothing # # here waits: every tool call either fails in argv parsing # # or is refused at open(2)/ioctl(2) and returns at once. # # rtc-range /dev/null walks its ten dates but `continue`s # # before each sleep(1), so it too returns immediately -- # # that is a property of the ioctl FAILING, and on an image # # that grows an RTC the same case becomes a ~10 s run, # # which the bound has room for. # rtc-tools -- an RTC-class userland on a kernel that builds no RTC subsystem. # # WHAT THIS MACHINE CAN AND CANNOT TEST. rtc-tools is three programs: rtc (a # twelve-command front end for the RTC_* ioctls), rtc-range (a driver date-range # test) and rtc-sync (a system-to-RTC offset measurer). All three open an # /dev/rtc* character device and drive it with the RTC_* ioctls from # linux/rtc.h. This kernel has no RTC subsystem at all -- RTC appears on none of # the 282 lines of arch/wasm32/configs/wasm32_defconfig, absent by omission # rather than by a "# CONFIG_RTC_CLASS is not set" annotation, because # CONFIG_RTC_CLASS selects a driver framework and this architecture has no # real-time clock for it to drive. So no rtc driver registers, no "rtc" line # appears in /proc/devices, /sys/class/rtc is absent and no /dev/rtc* node exists. # # WHAT IS THEREFORE ASSERTED FOR REAL, and it is most of rtc and all of both # other programs' argv handling: every command rtc dispatches, every path that # reaches its usage(), all sixteen lines of that usage text, the stream each # program writes to, and the exit status of every documented path. Those are # strcmp on argv and calls to fprintf; they have no device in them and are the # same on every architecture -- so a wasm32 build that got them wrong is a # wasm32 build that is wrong, with no environmental excuse available. # # AND THE DEEPEST REACHABLE POINT IS ASSERTED TOO, which is further in than the # gap suggests. open(2) on a character device that is not an RTC SUCCEEDS, so # handing any of rtc's twelve commands /dev/null carries it past the open into a # real RTC_* ioctl on a real descriptor. That is rtc.c executing, not merely # being present, and it is asserted against the errno the probe reads from the # same ioctl rather than against a transcribed string -- so the assertion # follows the kernel if the kernel moves. A directory is a second route to the # same ioctl and rtc-range /dev/null is a third. # # THE ONE UPSTREAM DEFECT THIS IMAGE CAN SEE, AND WHY IT IS ASSERTED AS A PAIR. # rtc and rtc-range both end their open-failure path with perror(rtc_file) then # exit(errno), intending to exit with the open error. glibc's perror does # fdopen(dup(2), "w+"): when stderr is O_RDWR -- a terminal -- that succeeds and # errno survives, so the process exits 2 (ENOENT); when stderr is a pipe or a # file it is write-only, the fdopen fails EINVAL, glibc falls back to writing fd # 2 directly, and the process exits 22. Traced both ways on x86-64 during the # port. brintos-selftest captures stderr through a command substitution, so this # suite always sees the 22 arm -- and it asserts the ENOENT MESSAGE and the # EINVAL STATUS together, because that contradiction IS the defect. Fixing it # upstream (saving errno across perror) reddens these cases rather than passing # quietly, which is the point of pinning it. # # WHAT IS PROBED RATHER THAN ASSUMED. The absence above is read at runtime from # ${libexecdir}/rtc-tools-tests/rtc-tools-probe and every branch below is # written against the live answer, so this suite stays correct the day the image # grows an RTC: the skips turn back into assertions on their own evidence rather # than on a comment. # # AND THE CONTROLS ARE THE POINT OF THE PROBE. "There is no RTC" and "this # process cannot open device nodes" produce the same refusal from all three # tools, so two independent controls separate them. /dev/null and /dev/zero are # opened; and a character node is created with mknod(2) at the legacy RTC misc # number (10,135) beside one at /dev/null's own (1,3), in the same directory. # The filesystem makes either inode whether or not a driver is registered, so if # the (1,3) node opens and the (10,135) node does not, the difference is the # absent driver and nothing else. Every skip below is gated on those controls; # if they stop holding, this suite reds rather than quietly skipping -- which is # the failure mode a suite full of environmental skips is otherwise built to # have. # # WHY THE RTC MAJOR IS PROBED BY MISC NUMBER AND NOT BY A FIXED CLASS MAJOR. The # rtc CLASS major is allocated dynamically at registration (248 on the x86-64 # build host, not a constant), so unlike spitools' spidev 153 there is no fixed # class number to synthesize. (10,135) is the one RTC node whose numbers are # architectural, so it is what the control pair is built from. bst_ok rtc-tools/suite-canary # first line: absorbs brintos/linux#96, which drops it require rtc rtc-tools/present # --- what upstream installs -------------------------------------------------- # # Three programs, from a single `install $(EXEC)` in a nine-line Makefile. That # install would fail the build if one were missing, so this is not an install # loop that can swallow one -- it is checked because the whole of the argv half # below addresses these three names, and a rename upstream would leave that half # skipping quietly rather than reddening. for t in rtc rtc-range rtc-sync; do if command -v "$t" >/dev/null 2>&1; then bst_ok "rtc-tools/present/$t" else bst_notok "rtc-tools/present/$t" "not on PATH -- upstream's Makefile installs rtc, rtc-range and rtc-sync to \${bindir}" fi done # --- the probe --------------------------------------------------------------- P=/usr/libexec/rtc-tools-tests/rtc-tools-probe if [ ! -x "$P" ]; then bst_notok rtc-tools/probe/present "no probe at $P -- rtc-tools-tests is not installed, so nothing below can be attributed" exit 0 fi PO=$("$P" 2>&1); prc=$? if bst_execfail "$PO"; then bst_unrun rtc-tools/probe/runs "$PO" exit 0 fi case "$PO" in *"probe end"*) bst_ok rtc-tools/probe/runs ;; *) bst_notok rtc-tools/probe/runs "rc=$prc, no 'probe end' -- the probe did not finish, so its values below are partial: $(_bst_1line "$PO")" exit 0 ;; esac # field -- the value of on the probe's # line. Reads from a variable, never a redirect: a top-level stdin # redirect on the virtiofs root makes dup2(2) return 513 and ash exits, ending # the suite silently (brintos/linux#73). field() { printf '%s\n' "$PO" | sed -n "s/^$1 / /p" \ | sed -n "s/.*[ ]$2=\\([^ ]*\\).*/\\1/p" | sed -n 1p } # state -- the third word of a ` present|absent` line. state() { printf '%s\n' "$PO" | sed -n "s/^$1 [^ ]* \\([a-z]*\\)$/\\1/p" | sed -n 1p } # tail_of -- everything after `=` on , kept # whole. strerror texts contain spaces, so `field` cannot carry them. tail_of() { printf '%s\n' "$PO" | sed -n "s/^$1 .*[ ]$2=//p" | sed -n 1p } null_rc=$(field control-dev-null rc) zero_rc=$(field control-dev-zero rc) mkrtc_rc=$(field mknod-chr rc); mkrtc_name=$(field mknod-chr name) oprtc_rc=$(field open-mknod-rtc rc); oprtc_name=$(field open-mknod-rtc name) mknull_rc=$(field control-mknod-null rc) opnull_rc=$(field control-open-mknod-null rc) rtc0_rc=$(field open-rtc0 rc); rtc0_name=$(field open-rtc0 name) n_rtc=$(field rtc-glob count) rtcmajor=$(field proc-devices-rtc major) classrtc=$(state sysfs-class-rtc) procrtc=$(state procfs-driver-rtc) ioctl_rc=$(field ioctl-rd-time-null rc) ioctl_name=$(field ioctl-rd-time-null name) ioctl_err=$(tail_of ioctl-rd-time-null strerror) # Echo what the probe answered, one line at a time, prefixed so it is greppable # and plainly not a result line. Costs nothing and saves the next reader of a # stored run a boot: every branch below is a function of these values, and # without them a stored dump records the verdicts without the evidence. printf '%s\n' "$PO" | while read -r _dl; do [ -n "$_dl" ] && printf '#diag: rtc-tools probe: %s\n' "$_dl" done # --- the controls ------------------------------------------------------------ # # Asserted, not recorded. While they pass, an absent RTC is a fact about the # kernel's RTC subsystem; if they ever fail, it is a fact about this process and # nothing below may be attributed to CONFIG_RTC_CLASS. check_eq rtc-tools/control/dev-null 0 "$null_rc" check_eq rtc-tools/control/dev-zero 0 "$zero_rc" check_eq rtc-tools/control/mknod 0 "$mknull_rc" check_eq rtc-tools/control/mknod-open 0 "$opnull_rc" # Two gates, deliberately not one. controls_open says this process can open a # character device at all -- that is everything the POSITIVE assertions below # need, because "rtc rd /dev/null reaches a real ioctl" stands or falls on # /dev/null alone. controls_ok adds the mknod pair, which is what makes an # ABSENCE attributable to a missing driver rather than to this process. Gating # the assertions on the stronger of the two would skip a whole block of # genuinely-assertable cases on any image where mknod is unavailable -- which is # skipping by category, the thing the probe exists to avoid. controls_open=no if [ "$null_rc" = 0 ] && [ "$zero_rc" = 0 ]; then controls_open=yes fi controls_ok=no if [ "$controls_open" = yes ] \ && [ "$mknull_rc" = 0 ] && [ "$opnull_rc" = 0 ]; then controls_ok=yes fi # --- the measurement --------------------------------------------------------- # # Recorded as a value with a definite answer required, rather than asserted in # one direction: both outcomes are legitimate results and a suite that demanded # the absence would red on the good day. rtcdev=unknown if [ "${n_rtc:-0}" -gt 0 ] 2>/dev/null; then rtcdev=present elif [ "$n_rtc" = 0 ]; then rtcdev=absent fi case "$rtcdev" in present|absent) bst_ok rtc-tools/rtc/answer ;; *) bst_notok rtc-tools/rtc/answer "the probe printed no usable rtc-glob line" ;; esac # The two sysfs/procfs places an RTC subsystem would show, and /proc/devices' # char major. Recorded rather than asserted absent for the same reason, but they # must AGREE with each other: a kernel with /sys/class/rtc and no rtc major is a # partial RTC build and a different situation from this one, and a suite that # only counted device nodes could not tell them apart. if [ "$classrtc" = absent ] && [ "$procrtc" = absent ] && [ "$rtcmajor" = "-1" ]; then bst_ok rtc-tools/rtc/sysfs-coherent elif [ "$classrtc" = present ] && [ "$rtcmajor" != "-1" ]; then bst_ok rtc-tools/rtc/sysfs-coherent else bst_notok rtc-tools/rtc/sysfs-coherent "the three RTC readings disagree: /sys/class/rtc=$classrtc /proc/driver/rtc=$procrtc /proc/devices rtc major=$rtcmajor. This is a partially-present RTC subsystem, which is neither the absence the skips below claim nor a working one" fi # The two mknod readings are the control pair that makes the absence # attributable, and they are a real assertion rather than a record: the # filesystem makes both inodes, so the ONLY thing that can separate them is # whether a driver is registered at the number. if [ "$mkrtc_rc" != 0 ]; then bst_notok rtc-tools/rtc/node-open "mknod(2) of a char node at the legacy RTC misc number (10,135) failed with $mkrtc_name while the (1,3) control node was made -- the two differ only in their device number, so this is not a filesystem or permission answer and the probe cannot attribute anything" elif [ "$controls_ok" != yes ]; then bst_notok rtc-tools/rtc/node-open "the controls did not pass (dev-null=$null_rc dev-zero=$zero_rc mknod=$mknull_rc mknod-open=$opnull_rc), so this process cannot open character devices either and no rtc-tools failure may be attributed to the absent RTC subsystem" elif [ "$rtcdev" = absent ] && [ "$oprtc_rc" = "-1" ]; then # The reading this image is expected to give: the inode exists, the (1,3) # node beside it opens, and this one does not -- because nothing is # registered at the RTC misc number. bst_ok rtc-tools/rtc/node-open elif [ "$rtcdev" = present ]; then bst_skip rtc-tools/rtc/node-open "this image carries $n_rtc /dev/rtc* node(s), so a synthesized node at (10,135) is no longer the interesting question -- assert against the real device instead" else bst_notok rtc-tools/rtc/node-open "a char node at (10,135) OPENED (rc=$oprtc_rc) on an image with no /dev/rtc* and no rtc major in /proc/devices -- something is registered there that the census cannot see" fi # --- argument parsing, usage text and exit status ---------------------------- # # None of these reaches a device node, so all of them run on their merits on any # kernel. rtc's usage() ends `exit(EINVAL)`, so 22 is the status of EVERY # argv-rejection path in the program and it is checked exactly -- rtc has no # success exit at all on this image, which is precisely what a "the tool ran" # assertion cannot see. # # The tools are invoked by BARE NAME throughout, because rtc and rtc-range both # interpolate argv[0] into their usage text -- `Usage: rtc `, # `usage: rtc-range [rtcdev]` -- so the strings asserted here are only correct # for a PATH invocation. rtc-tools/argv/argv0-path pins that dependence # deliberately by invoking rtc by absolute path and asserting the path comes back. apr() { # apr _aid=$1; _awant=$2; _aneedle=$3; shift 3 _aout=$("$@" 2>&1); _arc=$? case "$_aout" in *"$_aneedle"*) _ahit=1 ;; *) _ahit=0 ;; esac if bst_execfail "$_aout"; then bst_unrun "$_aid" "$_aout" elif [ "$_arc" -eq "$_awant" ] && [ "$_ahit" -eq 1 ]; then bst_ok "$_aid" else bst_notok "$_aid" "rc=$_arc want=$_awant want:'$_aneedle' got:'$(_bst_1line "$_aout")'"; fi } # stdout_empty : ok iff the command wrote NOTHING to stdout. # Everything rtc and rtc-range print on the paths this image reaches is a # diagnostic and belongs on stderr; rtc-sync is the one program that writes to # stdout, and it is asserted separately below. A tool that started announcing # its refusals on stdout would break every caller that pipes it, and no # combined-output assertion above could see it. stdout_empty() { _sid=$1; shift _sout=$("$@" 2>/dev/null) if [ -z "$_sout" ]; then bst_ok "$_sid" else bst_notok "$_sid" "stdout was not empty: '$(_bst_1line "$_sout")'"; fi } # -- rtc: every path that reaches usage(), each exiting EINVAL (22). # These are separate cases rather than one because they are separate BRANCHES # in main(): a missing command, an unknown command, six too-few-argument # checks and four parse_rtc_param() refusals. Collapsing them would let any # one of them fall through to the device open unnoticed. apr rtc-tools/argv/no-args 22 'Usage: rtc ' rtc apr rtc-tools/argv/bad-command 22 'Usage: rtc ' rtc no-such-command apr rtc-tools/argv/set-no-ts 22 'Usage: rtc ' rtc set apr rtc-tools/argv/wkalmset-no-ts 22 'Usage: rtc ' rtc wkalmset apr rtc-tools/argv/almset-no-ts 22 'Usage: rtc ' rtc almset apr rtc-tools/argv/paramget-no-args 22 'Usage: rtc ' rtc paramget apr rtc-tools/argv/paramget-one-arg 22 'Usage: rtc ' rtc paramget RTC_PARAM_FEATURES apr rtc-tools/argv/paramset-short 22 'Usage: rtc ' rtc paramset RTC_PARAM_CORRECTION 0 # parse_rtc_param()'s three distinct refusals. The first two are the unknown-name # arm; the third is the `default:` arm, reached only by naming a parameter that # IS known but takes no settable value -- a different line of the function, and # the one a test that only tried garbage names would never enter. apr rtc-tools/argv/paramget-bad-name 22 'Usage: rtc ' rtc paramget NO_SUCH_PARAM 0 apr rtc-tools/argv/paramset-bad-name 22 'Usage: rtc ' rtc paramset NO_SUCH_PARAM 0 1 apr rtc-tools/argv/paramset-features 22 'Usage: rtc ' rtc paramset RTC_PARAM_FEATURES 0 1 apr rtc-tools/argv/paramset-bad-bsm 22 'Usage: rtc ' rtc paramset RTC_PARAM_BACKUP_SWITCH_MODE 0 NO_SUCH_MODE # -- rtc: all sixteen lines of the usage text, each asserted on its own. # One needle per line, because a single "Usage:" needle would pass on a # program that had lost every command but the first. apr rtc-tools/usage/head 22 'Usage: rtc ' rtc apr rtc-tools/usage/rd 22 'rtc rd [rtc]' rtc apr rtc-tools/usage/set 22 'rtc set YYYY-MM-DDThh:mm:ss [rtc]' rtc apr rtc-tools/usage/wkalmrd 22 'rtc wkalmrd [rtc]' rtc apr rtc-tools/usage/wkalmset 22 'rtc wkalmset YYYY-MM-DDThh:mm:ss [rtc]' rtc apr rtc-tools/usage/almread 22 'rtc almread [rtc]' rtc apr rtc-tools/usage/almset 22 'rtc almset YYYY-MM-DDThh:mm:ss [rtc]' rtc apr rtc-tools/usage/aieon 22 'rtc aieon [rtc]' rtc apr rtc-tools/usage/aieoff 22 'rtc aieoff [rtc]' rtc apr rtc-tools/usage/vlrd 22 'rtc vlrd [rtc]' rtc apr rtc-tools/usage/vlclr 22 'rtc vlclr [rtc]' rtc apr rtc-tools/usage/paramget 22 'rtc paramget param index [rtc]' rtc apr rtc-tools/usage/paramset 22 'rtc paramset param index value [rtc]' rtc apr rtc-tools/usage/valid-hdr 22 'Valid parameters:' rtc apr rtc-tools/usage/param-features 22 '- RTC_PARAM_FEATURES' rtc apr rtc-tools/usage/param-correction 22 '- RTC_PARAM_CORRECTION' rtc apr rtc-tools/usage/param-bsm 22 '- RTC_PARAM_BACKUP_SWITCH_MODE' rtc # -- the stream discipline. Everything rtc and rtc-range emit is fprintf(stderr). stdout_empty rtc-tools/stream/usage-stdout-empty rtc stdout_empty rtc-tools/stream/nodev-stdout-empty rtc rd stdout_empty rtc-tools/stream/range-usage-stdout-empty rtc-range a b stdout_empty rtc-tools/stream/range-nodev-stdout-empty rtc-range # -- argv[0] interpolation, pinned deliberately. Every needle above is only # correct because the tool was invoked by bare name; this case is what records # that dependence rather than leaving it to be rediscovered. apr rtc-tools/argv/argv0-path 22 'Usage: /usr/bin/rtc ' /usr/bin/rtc # -- rtc-range: a different argv shape and a DIFFERENT status. It takes an # optional device and rejects any second argument with exit 1 -- not 22 -- # and its usage line is lowercase where rtc's is capitalised. The pair is the # assertion: two programs in one package disagreeing on both is upstream's # own inconsistency, and asserting only one of them would read as a typo. apr rtc-tools/range/usage-two-args 1 'usage: rtc-range [rtcdev]' rtc-range a b apr rtc-tools/range/usage-three-args 1 'usage: rtc-range [rtcdev]' rtc-range a b c apr rtc-tools/range/usage-lowercase 1 'usage: rtc-range' rtc-range a b # -- rtc-sync: the one program here that writes to stdout, and the one that # takes no arguments at all -- main(void), so argv is not merely unused but # unreachable. Both facts are asserted, because "ignores its arguments" and # "rejects its arguments" are different programs and the difference is # invisible from a run with none. sync_out=$(rtc-sync 2>/dev/null) sync_args_out=$(rtc-sync --bogus argument here 2>/dev/null) case "$sync_out" in *'CLOCK_REALTIME '*) bst_ok rtc-tools/sync/realtime-line ;; *) bst_notok rtc-tools/sync/realtime-line "no 'CLOCK_REALTIME ' on stdout: '$(_bst_1line "$sync_out")'" ;; esac case "$sync_out" in *'CLOCK_MONOTONIC '*) bst_ok rtc-tools/sync/monotonic-line ;; *) bst_notok rtc-tools/sync/monotonic-line "no 'CLOCK_MONOTONIC ' on stdout: '$(_bst_1line "$sync_out")'" ;; esac check_eq rtc-tools/sync/ignores-argv "$sync_out" "$sync_args_out" # The clock_getres lines are printed with "%d.%09d", so each carries exactly # nine digits after the point whatever the kernel's resolution turns out to be. # The FORMAT is the package's own and is asserted; the VALUE is the kernel's and # is not, because pinning it here would put a kernel fact in a package suite and # red this file the day the clock changes. case "$sync_out" in *'CLOCK_REALTIME '[0-9]*.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*) bst_ok rtc-tools/sync/res-format ;; *) bst_notok rtc-tools/sync/res-format "CLOCK_REALTIME line is not .<9 digits>: '$(_bst_1line "$sync_out")'" ;; esac # --- the documented failure when no RTC is present --------------------------- # # This is the ticket's subject, and it is asserted as a PAIR throughout: the # message names ENOENT and the status is EINVAL(22), which cannot both be right. # See the header -- perror clobbers the errno that exit(errno) then reads. Every # one of rtc's twelve commands is driven, because each reaches the open through # its own branch of main() and a command that stopped reaching it would otherwise # be invisible. if [ "$rtcdev" = absent ] && [ "$controls_open" = yes ]; then for c in rd wkalmrd almread aieon aieoff vlrd vlclr; do apr "rtc-tools/nodev/$c" 22 '/dev/rtc0: No such file or directory' rtc "$c" done apr rtc-tools/nodev/set 22 '/dev/rtc0: No such file or directory' rtc set 2020-01-01T00:00:00 apr rtc-tools/nodev/wkalmset 22 '/dev/rtc0: No such file or directory' rtc wkalmset 2020-01-01T00:00:00 apr rtc-tools/nodev/almset 22 '/dev/rtc0: No such file or directory' rtc almset 2020-01-01T00:00:00 apr rtc-tools/nodev/paramget 22 '/dev/rtc0: No such file or directory' rtc paramget RTC_PARAM_FEATURES 0 apr rtc-tools/nodev/paramset 22 '/dev/rtc0: No such file or directory' rtc paramset RTC_PARAM_CORRECTION 0 5 apr rtc-tools/nodev/range 22 '/dev/rtc0: No such file or directory' rtc-range # rtc-sync hardcodes /dev/rtc0, cannot be pointed elsewhere, and returns the # open's own -1 rather than errno -- so its no-RTC status is 255, a third # convention in a three-program package. Asserted because it is upstream's # documented behaviour on exactly this image's condition. apr rtc-tools/nodev/sync-status 255 'open: No such file or directory' rtc-sync # The device path is interpolated from argv, not fixed -- so the message # above is the tool reporting what it was asked for rather than a constant. apr rtc-tools/nodev/path-interpolated 22 '/no/such/rtc: No such file or directory' rtc rd /no/such/rtc # The pinned contradiction, stated once as its own case so that a reader of # the report meets it rather than deducing it from the pairs above. _po=$(rtc rd 2>&1); _prc=$? case "$_po" in *'No such file or directory'*) if [ "$_prc" -eq 22 ]; then bst_ok rtc-tools/nodev/errno-clobbered elif [ "$_prc" -eq 2 ]; then bst_notok rtc-tools/nodev/errno-clobbered "rtc rd exited 2 (ENOENT), which is what exit(errno) INTENDED -- upstream or glibc has changed so that perror no longer clobbers errno on a non-tty stderr. That is a fix, not a regression: re-read the header and drop this case" else bst_notok rtc-tools/nodev/errno-clobbered "rtc rd printed ENOENT but exited $_prc, which is neither 2 (errno preserved) nor 22 (perror's failed fdopen) -- a third value means the failure path changed shape" fi ;; *) bst_notok rtc-tools/nodev/errno-clobbered "rtc rd did not report ENOENT at all: '$(_bst_1line "$_po")'" ;; esac else # Name the condition that actually sent us here. The two causes call for # opposite responses -- an image that grew an RTC wants these rewritten as # assertions, a process that cannot open device nodes wants investigating -- # and a skip reason that guesses between them is worse than no reason. if [ "$rtcdev" != absent ]; then _why="this image has an RTC (rtc-glob=$n_rtc), so the no-device diagnostic is not what /dev/rtc0 produces here -- these cases want rewriting as assertions against the real device" else _why="this process cannot open a character device at all (control /dev/null rc=$null_rc, /dev/zero rc=$zero_rc), so a refusal from rtc-tools could not be attributed to the absent RTC" fi for c in rd wkalmrd almread aieon aieoff vlrd vlclr set wkalmset almset paramget paramset range sync-status path-interpolated errno-clobbered; do bst_skip "rtc-tools/nodev/$c" "$_why" done fi # --- the deepest reachable point: a real RTC ioctl on a real descriptor ------ # # open(2) on /dev/null succeeds, so each command below runs its full branch of # main(), performs its RTC_* ioctl on a live fd and reports the kernel's own # errno. This is rtc.c executing. The expected errno is taken from the PROBE, # which issued RTC_RD_TIME on /dev/null itself, so if the kernel ever answers # something other than ENOTTY these assertions move with it instead of reddening. if [ "$ioctl_rc" = "-1" ] && [ -n "$ioctl_name" ]; then bst_ok rtc-tools/ioctl/probe-baseline else bst_notok rtc-tools/ioctl/probe-baseline "the probe's own RTC_RD_TIME on /dev/null returned rc=$ioctl_rc name=$ioctl_name -- without a refusal from the same ioctl there is no baseline to assert the tools against" fi if [ "$controls_open" = yes ] && [ "$ioctl_rc" = "-1" ]; then # Each command's own ioctl name appears in upstream's IOCTL macro output, # so the needle proves WHICH ioctl ran -- not merely that one did. apr rtc-tools/ioctl/rd 25 "RTC_RD_TIME returned $ioctl_err" rtc rd /dev/null apr rtc-tools/ioctl/set 25 "RTC_SET_TIME returned $ioctl_err" rtc set 2020-01-01T00:00:00 /dev/null apr rtc-tools/ioctl/wkalmrd 25 "RTC_WKALM_RD returned $ioctl_err" rtc wkalmrd /dev/null apr rtc-tools/ioctl/wkalmset 25 "RTC_WKALM_SET returned $ioctl_err" rtc wkalmset 2020-01-01T00:00:00 /dev/null apr rtc-tools/ioctl/almread 25 "RTC_ALM_READ returned $ioctl_err" rtc almread /dev/null apr rtc-tools/ioctl/almset 25 "RTC_ALM_SET returned $ioctl_err" rtc almset 2020-01-01T00:00:00 /dev/null apr rtc-tools/ioctl/aieon 25 "RTC_AIE_ON returned $ioctl_err" rtc aieon /dev/null apr rtc-tools/ioctl/aieoff 25 "RTC_AIE_OFF returned $ioctl_err" rtc aieoff /dev/null apr rtc-tools/ioctl/vlrd 25 "RTC_VL_READ returned $ioctl_err" rtc vlrd /dev/null apr rtc-tools/ioctl/vlclr 25 "RTC_VL_CLR returned $ioctl_err" rtc vlclr /dev/null apr rtc-tools/ioctl/paramget 25 "RTC_PARAM_GET returned $ioctl_err" rtc paramget RTC_PARAM_FEATURES 0 /dev/null apr rtc-tools/ioctl/paramset 25 "RTC_PARAM_SET returned $ioctl_err" rtc paramset RTC_PARAM_CORRECTION 0 5 /dev/null # The errno is reported numerically as well as by strerror, from the same # macro. Asserted separately so a strerror table change cannot mask a # changed errno, and vice versa. apr rtc-tools/ioctl/errno-numeric 25 '(25) at line' rtc rd /dev/null # A directory is a second, independent route to the same ioctl: open(2) # succeeds on it O_RDONLY, so this reaches the identical code by a path that # shares nothing with /dev/null but the fact that it is not an RTC. apr rtc-tools/ioctl/directory 25 "RTC_RD_TIME returned $ioctl_err" rtc rd /tmp # rtc-range is the third route, and the only one that walks a LOOP: it drives # all ten of its date cases, each failing RTC_SET_TIME, and -- unlike rtc -- # its IOCTL macro `continue`s instead of exiting, so it ends 0 having printed # ten refusals. That difference in the shared macro is upstream's, and a # build in which rtc-range started exiting on the first failure would still # pass every rtc case above. range_out=$(rtc-range /dev/null 2>&1); range_rc=$? check_eq rtc-tools/range/null-rc 0 "$range_rc" case "$range_out" in *'Testing 1970-01-01 00:00:00.'*) bst_ok rtc-tools/range/first-date ;; *) bst_notok rtc-tools/range/first-date "no 'Testing 1970-01-01 00:00:00.' in rtc-range /dev/null output: '$(_bst_1line "$range_out")'" ;; esac case "$range_out" in *'Testing 2262-04-11 23:47:16.'*) bst_ok rtc-tools/range/last-date ;; *) bst_notok rtc-tools/range/last-date "no 'Testing 2262-04-11 23:47:16.' -- rtc-range stopped before its tenth date, so its loop did not complete" ;; esac case "$range_out" in *'KO RTC_SET_TIME returned 25'*) bst_ok rtc-tools/range/ko-line ;; *) bst_notok rtc-tools/range/ko-line "no 'KO RTC_SET_TIME returned 25' -- rtc-range did not reach its ioctl: '$(_bst_1line "$range_out")'" ;; esac _n_testing=$(printf '%s\n' "$range_out" | grep -c '^Testing ') check_eq rtc-tools/range/all-ten-dates 10 "$_n_testing" _n_ko=$(printf '%s\n' "$range_out" | grep -c '^KO RTC_SET_TIME') check_eq rtc-tools/range/ten-refusals 10 "$_n_ko" else for c in rd set wkalmrd wkalmset almread almset aieon aieoff vlrd vlclr paramget paramset errno-numeric directory; do bst_skip "rtc-tools/ioctl/$c" "/dev/null could not be opened or the probe's own RTC_RD_TIME on it did not refuse (control /dev/null rc=$null_rc, ioctl-rd-time-null rc=$ioctl_rc), so there is no live ioctl baseline to assert the tools against" done for c in null-rc first-date last-date ko-line all-ten-dates ten-refusals; do bst_skip "rtc-tools/range/$c" "same: no ioctl baseline from the probe" done fi # --- what genuinely needs an RTC --------------------------------------------- # # These are the device-driven paths. Each is skipped ONLY on the probe's live # answer plus the controls, never by category, and each names a different thing # that becomes assertable the day this image grows an RTC -- so the suite states # what is missing rather than merely being shorter. if [ "$rtcdev" = present ] && [ "$controls_ok" = yes ] && [ "$rtc0_rc" = 0 ]; then bst_notok rtc-tools/device/available "this image now has an openable /dev/rtc0 (rtc-glob=$n_rtc, open-rtc0=$rtc0_name). The device-driven cases below were written as skips against an absent RTC and must now be written as assertions -- see the ticket that ported this package" else for d in \ "rd:reading the wall time back through RTC_RD_TIME" \ "set:setting it through RTC_SET_TIME and reading it back" \ "range:rtc-range's ten-date driver range walk, including the two leap years and the three time_t overflows" \ "alarm:the wakealarm round trip (RTC_WKALM_SET/RTC_WKALM_RD) and RTC_AIE_ON/OFF" \ "vl:the voltage-low flags (RTC_VL_READ/RTC_VL_CLR) and their five decoded messages" \ "param:RTC_PARAM_GET/SET for features, correction and backup switch mode, with the feature-bit decode" \ "sync:rtc-sync's system-to-RTC offset measurement, which needs a running clock to have an offset from" \ ; do _dn=${d%%:*}; _dd=${d#*:} bst_skip "rtc-tools/device/$_dn" "no RTC on this image -- $_dd needs one. Measured: /dev/rtc* count=$n_rtc, /sys/class/rtc=$classrtc, /proc/driver/rtc=$procrtc, /proc/devices rtc major=$rtcmajor, and a synthesized char node at (10,135) opened ${oprtc_name:-} while the (1,3) node beside it opened ${opnull_rc:-}. RTC is absent from all 282 lines of arch/wasm32/configs/wasm32_defconfig" done fi