. "$BST_LIB" # mingetty 1.08 (oe-core recipes-extended) -- a getty: the program init runs on a # console to print the issue banner, collect a login name and exec login(1). # Three platform facts shape every case below. # # 1. IT SPEAKS ONLY TO SYSLOG. mingetty's error() does openlog(LOG_AUTH) + # vsyslog() and exits 1 -- it writes NOTHING to stderr. A mingetty that fails # is therefore SILENT on the console, and asserting on any diagnostic of its # own means standing up a syslogd first (sysklogd, already on this image). # The one thing that does reach stderr is getopt_long's "unrecognized option", # which is glibc talking, not mingetty. # # 2. IT NEEDS A TERMINAL, AND IT MUST NOT BE THE ONE THE RUNNER IS ON. # open_tty() opens the device by name, takes it as the controlling terminal # and refuses to go on unless isatty() agrees, so no shell pipeline can drive # the login flow -- and pointing it at the console this suite is reporting # through would seize that console and end the run. Two terminals answer # that: a pty from expect(1) (data/selftest_mingetty.exp), and /dev/tty1, # which is a real virtual console on this kernel and is what the package # actually exists for. # # 3. IT IS NOT /sbin/getty HERE. The recipe registers the `getty` alternative at # priority 10 and busybox registers the same name at 50, so the generic name # stays busybox and this port is reachable only as `mingetty`. That is the # intended outcome, and getty-alternative below is what pins it. require mingetty mingetty/present D=/tmp/.bst-mingetty rm -rf "$D"; mkdir -p "$D" # wait_for -- bounded poll, never a sleep. sleep(1) does not # sleep on this image (~0.29 s whatever the argument, linux-in-the-browser#120), # so the iteration bound is what keeps a failing wait inside the runner's 60 s # suite budget; the sleep only yields. wait_for() { _f=$1; _n=$2; _i=0 while [ $_i -lt 20 ]; do grep -q "$_n" "$_f" 2>/dev/null && return 0 _i=$((_i + 1)); sleep 1 done return 1 } # --- identity and inventory --------------------------------------------------- # base_sbindir, not sbindir: the recipe seds the upstream Makefile's SBINDIR and # installs into /sbin, which is also where the alternatives link would have gone. check_eq mingetty/resolves-to-port "/sbin/mingetty" "$(command -v mingetty)" # busybox owns the generic name (fact 3). Reading the link rather than running # `getty` is deliberate -- busybox getty would try to open a terminal. gl=$(readlink /sbin/getty 2>/dev/null) case "$gl" in *busybox*) bst_ok mingetty/getty-alternative ;; "") bst_notok mingetty/getty-alternative "/sbin/getty is not a symlink; mingetty's priority-10 alternative may have taken the name" ;; *) bst_notok mingetty/getty-alternative "/sbin/getty -> $gl (expected busybox: priority 50 beats mingetty's 10)" ;; esac # --- the argument contract, no terminal required ------------------------------ # Missing tty operand: usage() -> error() -> exit 1. Both halves are asserted, # because the silence is as much the contract as the status is (fact 1) and a # caller that waits for a message on stderr waits forever. out=$(mingetty 2>&1); rc=$? check_eq mingetty/usage-rc "1" "$rc" check_eq mingetty/usage-silent "" "$(_bst_1line "$out")" # An unknown option is the one case with console output, and it comes from # getopt_long, not from mingetty: opterr is left at its default so glibc prints # the diagnostic itself, then mingetty's own default: arm exits 1 through the # same silent usage() path. out=$(mingetty --no-such-option tty9 2>&1); rc=$? case "$out" in *"unrecognized option"*) hit=1 ;; *) hit=0 ;; esac if [ "$rc" -eq 1 ] && [ "$hit" -eq 1 ]; then bst_ok mingetty/badopt else bst_notok mingetty/badopt "rc=$rc (want 1) getopt_diag=$hit: $(_bst_1line "$out")"; fi # A tty that does not exist: chown(2) fails ENOENT before the open, so this is # the first line of open_tty() failing, not the open itself. check_rc mingetty/nodev-rc 1 mingetty --nohangup /dev/bst-nosuch-tty # --- a syslogd of our own, for the cases that need to hear mingetty ----------- # Stood up rather than assumed (fact 1), and proved to own /dev/log with a probe # of this suite's own before any silence from mingetty is believed -- an earlier # suite's leftover daemon holding the socket would otherwise read as mingetty # saying nothing. It stays up until the last case that might need to explain # itself. LOG=$D/auth.log HAVE_LOG=0 SP="" if command -v syslogd >/dev/null 2>&1; then printf '*.*\t-%s\n' "$LOG" > "$D/mg.conf" syslogd -F -K -m 0 -P "$D/pid" -f "$D/mg.conf" & SP=$! logger -t bstmgprobe -p auth.err "MINGETTY-PROBE-9701" wait_for "$LOG" "MINGETTY-PROBE-9701" && HAVE_LOG=1 fi # why -- the daemon's last word about mingetty, for a failure detail. why() { [ "$HAVE_LOG" -eq 1 ] || { printf 'no syslogd of our own to ask'; return; } wait_for "$LOG" "$1" >/dev/null 2>&1 _bst_1line "$(grep mingetty "$LOG" 2>/dev/null | tail -1)" } # --- utmp --------------------------------------------------------------------- # update_utmp() runs BEFORE open_tty(), so a mingetty that cannot reach its # terminal has still written its LOGIN_PROCESS record -- which is what makes the # record observable here at all. glibc's pututline() does not create the file, # and this image ships no utmp database (/var/run -> ../run, and nothing puts a # utmp in it), so on a stock boot the update is a no-op by construction. The case # creates an empty database first, and removes it again, so the write itself is # assertable and the image is left as it was found. # # When the write does not land the reason is one layer down: glibc's pututline() # takes an F_SETLKW advisory lock on the file first (try_file_lock() in # login/utmp_file.c) and returns NULL WITHOUT WRITING if it cannot get one -- and # fcntl advisory locks fail EINVAL on this kernel, the same defect dpkg already # carries dpkg-wasm-nolock.patch for. So the verdict is decided by probing that # capability rather than assuming it: a lock that works and a record that still # does not land is a real failure, and says so. UT=/var/run/utmp created=0 if [ ! -e "$UT" ]; then : > "$UT" 2>/dev/null && created=1; fi if [ -f "$UT" ] && [ -w "$UT" ]; then mingetty --nohangup /dev/bstty0 >/dev/null 2>&1 # Fixed-width NUL-padded fields: turning NULs into newlines puts ut_line and # ut_user on lines of their own. fields=$(tr '\000' '\n' < "$UT" 2>/dev/null) case "$fields" in *bstty0*) l=1 ;; *) l=0 ;; esac case "$fields" in *LOGIN*) u=1 ;; *) u=0 ;; esac if [ "$l" -eq 1 ] && [ "$u" -eq 1 ]; then bst_ok mingetty/utmp-record else # Does POSIX record locking work at all? perl's Fcntl constants come from # the same headers glibc compiled against, so this is the same F_SETLK the # C library issues. lockerr=$(_bst_1line "$(perl -e 'use Fcntl qw(F_SETLK F_WRLCK SEEK_SET); open(my $fh, "+<", $ARGV[0]) or do { print "open: $!"; exit }; my $fl = pack("s s x4 q q l x4", F_WRLCK, SEEK_SET, 0, 0, 0); # a named lvalue: fcntl writes back my $r = fcntl($fh, F_SETLK, $fl); print defined($r) ? "ok" : "$!";' "$UT" 2>&1)") # The verdict below is the lock probe's, so a probe that could not be staged # decides nothing — reporting a definite failure from it would claim a # measurement that was never taken. if bst_execfail "$lockerr"; then bst_unrun mingetty/utmp-record "$lockerr" else case "$lockerr" in *"Invalid argument"*|*"Function not implemented"*|*"No locks available"*) bst_skip mingetty/utmp-record \ "glibc pututline() takes an F_SETLKW lock before writing and returns without writing when it cannot; fcntl advisory locks fail here ('$lockerr') -- the wasm32 syscall table wires sys_fcntl but no sys_fcntl64, and on this 32-bit arch with __TIMESIZE=64 glibc's F_SETLK/F_SETLKW/F_GETLK are unconditionally the LFS numbers 13/14/12, which only sys_fcntl64 accepts (same gap as dpkg-wasm-nolock.patch)" ;; *) bst_notok mingetty/utmp-record "ut_line=bstty0 found=$l ut_user=LOGIN found=$u in $UT ($(wc -c < "$UT" 2>/dev/null) B); fcntl lock probe said '$lockerr'" ;; esac fi fi else bst_skip mingetty/utmp-record "no writable $UT on this image and one could not be created" fi [ "$created" -eq 1 ] && rm -f "$UT" # --- the diagnostic path: syslog ---------------------------------------------- # The only way to read what mingetty has to say (fact 1). if [ "$HAVE_LOG" -eq 1 ]; then mingetty --nohangup /dev/bst-nosuch-tty >/dev/null 2>&1 if wait_for "$LOG" "bst-nosuch-tty: No such file or directory"; then bst_ok mingetty/syslog-diagnostic else bst_notok mingetty/syslog-diagnostic "no LOG_AUTH line from mingetty: $(_bst_1line "$(tail -3 "$LOG" 2>&1)")" fi # The tag is argv[0] through openlog(progname, LOG_PID, LOG_AUTH). if grep -q "mingetty\[" "$LOG" 2>/dev/null; then bst_ok mingetty/syslog-tag else bst_notok mingetty/syslog-tag "no 'mingetty[pid]' tag in $LOG"; fi elif command -v syslogd >/dev/null 2>&1; then bst_skip mingetty/syslog-diagnostic "this run could not own /dev/log (another syslogd holds it)" bst_skip mingetty/syslog-tag "this run could not own /dev/log (another syslogd holds it)" else bst_skip mingetty/syslog-diagnostic "syslogd not installed; mingetty reports only through syslog" bst_skip mingetty/syslog-tag "syslogd not installed; mingetty reports only through syslog" fi # --- the real thing: a virtual console ---------------------------------------- # What the package exists for. /dev/tty0../dev/tty63 are real nodes on this # kernel, so the invocation an inittab would use can be run as itself rather than # only in the pty stand-in. Three things make it assertable without any way to # read tty1's output: # - mingetty EXECS the login program, so mingetty's exit status IS the login # program's. /bin/true therefore turns the whole chain -- open the console, # TIOCSCTTY it, print, hand off -- into one status. # - setsid is required, not decoration: TIOCSCTTY refuses a process that is not # a session leader, and this suite's shell is not one. # - timeout bounds a console nothing is draining. # This is also the only case that runs the DEFAULT hangup path against a real # console rather than a pty. if [ -c /dev/tty1 ]; then timeout 20 setsid mingetty --noclear --nonewline --noissue --nohostname \ --autologin bstroot --loginprog=/bin/true /dev/tty1 /dev/null 2>&1 rc=$? case "$rc" in 0) bst_ok mingetty/vc-tty1 ;; 124) bst_notok mingetty/vc-tty1 "timed out on /dev/tty1 (mingetty blocked rather than exec'ing the login program)" ;; *) bst_notok mingetty/vc-tty1 "rc=$rc on /dev/tty1; syslog says: $(why "tty1")" ;; esac else bst_skip mingetty/vc-tty1 "/dev/tty1 is not a character device on this kernel" fi [ -n "$SP" ] && kill "$SP" 2>/dev/null # --- the login flow, on a pty ------------------------------------------------- # See data/selftest_mingetty.exp for why expect is the only way in. Each case # below is one tagged line the payload printed. if command -v expect >/dev/null 2>&1; then PTY_OUT=$(expect -f "$BST_DATA/selftest_mingetty.exp" 2>&1) # expect is one of the largest binaries on the image, so it is among the first # things the kernel refuses to stage once the guest is fragmented. All seven # cases below read the same capture, so one unstaged expect would otherwise # report seven independent login-flow failures. pty_case() { if bst_execfail "$PTY_OUT"; then bst_unrun "$1" "$PTY_OUT"; return; fi _v=$(printf '%s\n' "$PTY_OUT" | awk -v k="$2" '$1==k{print $2}') if [ "$_v" = "1" ]; then bst_ok "$1" elif [ -z "$_v" ]; then bst_notok "$1" "payload printed no $2 line: $(_bst_1line "$PTY_OUT")" else bst_notok "$1" "$2=0: $(_bst_1line "$PTY_OUT")"; fi } pty_case mingetty/pty-autologin-announce R1-autologin-announce pty_case mingetty/pty-autologin-exec R1-autologin-exec pty_case mingetty/pty-prompt R2-prompt pty_case mingetty/pty-login-exec R2-exec pty_case mingetty/pty-issue-text R3-issue-text pty_case mingetty/pty-issue-tty-escape R3-issue-tty pty_case mingetty/pty-default-path R4-default-path-exec else for c in pty-autologin-announce pty-autologin-exec pty-prompt pty-login-exec \ pty-issue-text pty-issue-tty-escape pty-default-path; do bst_skip "mingetty/$c" "expect not installed; the login flow needs a pty" done fi # --- what this image cannot reach --------------------------------------------- bst_skip mingetty/vhangup "vhangup(2) is patched out of this port: __NR_vhangup (153) is declared in the wasm32 uapi but absent from arch/wasm32/kernel/syscall_table.c, and nothing provides the symbol either -- wasm-ld resolves it to a trapping 'signature_mismatch:vhangup' stub, so calling it kills the process instead of returning ENOSYS. vc-tty1 and pty-default-path cover the rest of the hangup sequence" bst_skip mingetty/man-page "mingetty.8 is packaged into mingetty-doc, and this image installs no -doc packages" rm -rf "$D"