# selftest_mingetty.exp -- exercise mingetty(8) on a real pty. # # Driven by tests/88-mingetty.test, in the tagged-value shape 40-tcl.test # established: one "R- <0|1>" line per assertion, and ALL-MINGETTY-OK # only when every one of them is 1. # # Why expect is needed at all. mingetty is a getty: it opens a terminal BY NAME, # makes it its controlling terminal (TIOCSCTTY) and refuses to go on unless # isatty() agrees, so no plain shell pipeline can drive it. The pty expect # allocates for the spawned process is handed straight back to mingetty as its # tty argument -- the shell resolves $(tty) on that pty and execs mingetty onto # it, so the process is already the session leader of the very terminal it is # asked to open. The console the runner is reporting on is never touched, which # is the other half of the reason: a getty pointed at the console would seize # it, and the run would end mid-suite with no output. # # --loginprog=/bin/echo stands in for login(1). mingetty's last act is # execl(loginprog, loginprog, autologin ? "-f" : "--", name, NULL) # so echo turns the hand-off itself into the observable: "-f " for an # automatic login, "-- " for a name typed at the prompt. The real # /bin/login here is busybox's, which would prompt for a password against a # shadow file this image has no usable entry in. set timeout 20 log_user 0 set MG /sbin/mingetty # Quiet flags shared by every case: no vhangup (case 4 is where that is tested # on its own), no ESC-c terminal reset, no leading newline, no /etc/issue and no # hostname -- so each case's output is exactly the one line it asserts on, and # case 3 is the only one that lets the banner through. set QUIET "--nohangup --noclear --nonewline --noissue --nohostname" proc has {hay needle} { expr {[string first $needle $hay] >= 0} } # drive -- run mingetty on its own pty and return everything # it wrote there. non-empty waits for the login prompt first and # answers it; the text consumed by that wait is folded back into the result so # the caller can assert on the prompt too. proc drive {opts sendline} { global MG set out "" if {[catch {spawn /bin/sh -c "exec $MG $opts \"\$(tty)\""} err]} { return "SPAWN-FAILED $err" } if {$sendline ne ""} { expect { "login: " { append out $expect_out(buffer); send "$sendline\r" } timeout { } eof { } } } expect { -re ".+" { append out $expect_out(buffer); exp_continue } timeout { } eof { } } catch { wait } return $out } set fails 0 proc report {name cond} { global fails set v [expr {$cond ? 1 : 0}] if {!$v} { incr fails } puts "$name $v" } # --- 1. automatic login ------------------------------------------------------- # The whole open_tty() path in one case: chown/chmod the device, open it, take it # as the controlling terminal, dup2 onto 0/1/2 -- then announce the login and # exec. Everything after this depends on it, so a zero here explains the rest. set o1 [drive "$QUIET --autologin bstroot --loginprog=/bin/echo" ""] report R1-autologin-announce [has $o1 "login: bstroot (automatic login)"] report R1-autologin-exec [has $o1 "-f bstroot"] # --- 2. a name typed at the prompt ------------------------------------------- # get_logname(): tcflush, print "login: ", then read the terminal a byte at a # time until CR. The "--" argv proves the non-autologin branch of the same execl. set o2 [drive "$QUIET --loginprog=/bin/echo" "bstuser"] report R2-prompt [has $o2 "login: "] report R2-exec [has $o2 "-- bstuser"] # --- 3. /etc/issue and its escapes ------------------------------------------- # Dropping --noissue and --nohostname turns do_prompt() into its full form: # print /etc/issue with backslash escapes expanded, then the hostname. The image # ships "brintOS (Yocto/wasm32) 0.1 \n \l", so \n (nodename) and \l (this tty) # are both exercised -- and \l is what proves the expansion ran at all rather # than the file being copied through verbatim. set o3 [drive "--nohangup --noclear --nonewline --autologin bstroot --loginprog=/bin/echo" ""] report R3-issue-text [has $o3 "brintOS"] report R3-issue-tty [has $o3 "pts/"] # --- 4. the DEFAULT path (no --nohangup) -------------------------------------- # Every case above passes --nohangup, so none of them covers the invocation a # real init actually uses. Left to itself mingetty closes 0/1/2, reopens the # device as fd 0 and retakes it with TIOCSCTTY -- preceded upstream by a # vhangup(2) that this port patches out, because on wasm32-hwjs the symbol is # undefined and wasm-ld's trapping stub kills the process outright rather than # failing (see the bbappend). This case is what proves the default invocation # runs at all, so it is deliberately the only one that leaves the flag off. set o4 [drive "--noclear --nonewline --noissue --nohostname --autologin bstroot --loginprog=/bin/echo" ""] report R4-default-path-exec [has $o4 "-f bstroot"] if {$fails == 0} { puts "ALL-MINGETTY-OK" } else { puts "MINGETTY-FAILS $fails" }