37c2ecece4
Writing startup diagnostics to stderr interleaves with the interactive console on the live image; /scheme/debug reaches the serial log without disturbing the shell's tty.
191 lines
8.3 KiB
Diff
191 lines
8.3 KiB
Diff
diff --git a/brush-interactive/src/minimal/input_backend.rs b/brush-interactive/src/minimal/input_backend.rs
|
|
index 48732fb..70b4049 100644
|
|
--- a/brush-interactive/src/minimal/input_backend.rs
|
|
+++ b/brush-interactive/src/minimal/input_backend.rs
|
|
@@ -48,15 +48,53 @@ impl MinimalInputBackend {
|
|
}
|
|
|
|
fn read_input_line() -> Result<ReadResult, ShellError> {
|
|
- let mut input = String::new();
|
|
- let bytes_read = std::io::stdin()
|
|
- .read_line(&mut input)
|
|
- .map_err(ShellError::InputError)?;
|
|
-
|
|
- if bytes_read == 0 {
|
|
- Ok(ReadResult::Eof)
|
|
- } else {
|
|
- Ok(ReadResult::Input(input))
|
|
+ // On Redox the interactive console is a pty slave whose reads can
|
|
+ // return Ok(0) with no bytes even though the master side is still open
|
|
+ // (e.g. a non-blocking read with no data currently queued). std's
|
|
+ // `read_line` reports that as end-of-input; if the shell treated it as
|
|
+ // EOF it would exit immediately and getty would respawn login in a
|
|
+ // tight loop. Distinguish a *spurious* empty read (retry, briefly
|
|
+ // yielding to avoid a busy spin) from a real terminal by only treating
|
|
+ // 0 bytes as EOF when stdin is not a tty.
|
|
+ use std::io::ErrorKind;
|
|
+ let mut dbg_iters: u32 = 0;
|
|
+ loop {
|
|
+ let mut input = String::new();
|
|
+ let dbg_res = std::io::stdin().read_line(&mut input);
|
|
+ if dbg_iters < 6 {
|
|
+ use std::io::Write;
|
|
+ let _ = std::io::stderr().write_all(
|
|
+ format!(
|
|
+ "BRUSH-DBG: read iter={} tty={} res={:?}\n",
|
|
+ dbg_iters,
|
|
+ std::io::stdin().is_terminal(),
|
|
+ dbg_res.as_ref().map(|n| *n).map_err(|e| e.kind()),
|
|
+ )
|
|
+ .as_bytes(),
|
|
+ );
|
|
+ }
|
|
+ dbg_iters = dbg_iters.wrapping_add(1);
|
|
+ match dbg_res {
|
|
+ // Got a line.
|
|
+ Ok(n) if n != 0 => return Ok(ReadResult::Input(input)),
|
|
+ // Zero bytes: real EOF only on a non-interactive input. On an
|
|
+ // interactive Redox pty a non-blocking read with no data queued
|
|
+ // can also surface as Ok(0); retry rather than exiting (which
|
|
+ // would drop back to a getty login loop).
|
|
+ Ok(_) => {
|
|
+ if !std::io::stdin().is_terminal() {
|
|
+ return Ok(ReadResult::Eof);
|
|
+ }
|
|
+ }
|
|
+ // A non-blocking read with no data currently available (or an
|
|
+ // interrupted syscall) is not fatal on an interactive terminal;
|
|
+ // keep polling for input.
|
|
+ Err(e)
|
|
+ if std::io::stdin().is_terminal()
|
|
+ && matches!(e.kind(), ErrorKind::WouldBlock | ErrorKind::Interrupted) => {}
|
|
+ Err(e) => return Err(ShellError::InputError(e)),
|
|
+ }
|
|
+ std::thread::sleep(std::time::Duration::from_millis(20));
|
|
}
|
|
}
|
|
}
|
|
diff --git a/brush-shell/src/entry.rs b/brush-shell/src/entry.rs
|
|
index 15340ad..46774c3 100644
|
|
--- a/brush-shell/src/entry.rs
|
|
+++ b/brush-shell/src/entry.rs
|
|
@@ -117,8 +117,18 @@ impl CommandLineArgs {
|
|
}
|
|
}
|
|
|
|
+/// Temporary mini-diagnostic: write to fd 2 (console) so brush startup is
|
|
+/// visible on serial.
|
|
+#[doc(hidden)]
|
|
+fn rb_dbg(m: &str) {
|
|
+ use std::io::Write;
|
|
+ let df = std::fs::OpenOptions::new().write(true).open("/scheme/debug");
|
|
+ if let Ok(mut df) = df { let _ = df.write_all(m.as_bytes()); }
|
|
+}
|
|
+
|
|
/// Main entry point for the `brush` shell.
|
|
pub fn run() {
|
|
+ rb_dbg("BRUSH-DBG: run() entered\n");
|
|
//
|
|
// Install the bundled-command registry so it's available both for
|
|
// bundled dispatch (handled next) and for builtin shim registration
|
|
@@ -173,17 +183,30 @@ pub fn run() {
|
|
//
|
|
// Run.
|
|
//
|
|
- #[cfg(any(unix, windows))]
|
|
+ // Redox's tokio multi-threaded runtime fails to build (EINVAL on worker
|
|
+ // thread setup); the current-thread runtime is the correct model there and
|
|
+ // is all an interactive shell needs.
|
|
+ #[cfg(all(any(unix, windows), not(target_os = "redox")))]
|
|
let mut builder = tokio::runtime::Builder::new_multi_thread();
|
|
- #[cfg(not(any(unix, windows)))]
|
|
+ #[cfg(any(not(any(unix, windows)), target_os = "redox"))]
|
|
let mut builder = tokio::runtime::Builder::new_current_thread();
|
|
|
|
- let Ok(runtime) = builder.enable_all().build() else {
|
|
- tracing::error!("error: failed to create Tokio runtime");
|
|
- std::process::exit(1);
|
|
+ rb_dbg("BRUSH-DBG: building tokio runtime (enable_all)\n");
|
|
+ let runtime = match builder.enable_all().build() {
|
|
+ Ok(rt) => {
|
|
+ rb_dbg("BRUSH-DBG: tokio runtime built OK\n");
|
|
+ rt
|
|
+ }
|
|
+ Err(e) => {
|
|
+ rb_dbg(&format!("BRUSH-DBG: tokio enable_all build FAILED: {e}\n"));
|
|
+ tracing::error!("error: failed to create Tokio runtime");
|
|
+ std::process::exit(1);
|
|
+ }
|
|
};
|
|
|
|
+ rb_dbg("BRUSH-DBG: entering block_on(run_async)\n");
|
|
let result = runtime.block_on(run_async(&args, parsed_args));
|
|
+ rb_dbg("BRUSH-DBG: block_on returned (shell exiting)\n");
|
|
|
|
let exit_code = match result {
|
|
Ok(code) => code,
|
|
@@ -242,6 +265,7 @@ async fn run_async(
|
|
cli_args: &[String],
|
|
args: CommandLineArgs,
|
|
) -> Result<u8, brush_interactive::ShellError> {
|
|
+ rb_dbg("BRUSH-DBG: run_async: entered\n");
|
|
// Initializing tracing.
|
|
let mut event_config = TRACE_EVENT_CONFIG.lock().await;
|
|
*event_config = Some(events::TraceEventConfig::init(
|
|
@@ -249,15 +273,18 @@ async fn run_async(
|
|
&args.disabled_events,
|
|
));
|
|
drop(event_config);
|
|
+ rb_dbg("BRUSH-DBG: run_async: tracing init done, loading config\n");
|
|
|
|
// Load configuration file.
|
|
let file_config = config::load_config(args.no_config, args.config_file.as_deref())
|
|
.into_config_or_log()
|
|
.map_err(|e| brush_interactive::ShellError::IoError(std::io::Error::other(e)))?;
|
|
+ rb_dbg("BRUSH-DBG: run_async: config loaded\n");
|
|
|
|
// Instantiate an appropriately configured shell and wrap it in an `Arc`. Note that we do
|
|
// *not* run any code in the shell yet. We'll delay loading profiles and such until after
|
|
// we've set up everything else (in `run_in_shell`).
|
|
+ rb_dbg("BRUSH-DBG: run_async: instantiating shell\n");
|
|
let shell: BrushShell = instantiate_shell(&args, cli_args).await?;
|
|
let shell = Arc::new(Mutex::new(shell));
|
|
|
|
@@ -265,6 +292,16 @@ async fn run_async(
|
|
// backend type and calls `run_in_shell`, preserving static dispatch.
|
|
let default_backend = get_default_input_backend_type(&args);
|
|
let selected_backend = args.input_backend.unwrap_or(default_backend);
|
|
+ rb_dbg(&format!(
|
|
+ "BRUSH-DBG: backend={} stdin_tty={} interactive={}\n",
|
|
+ match selected_backend {
|
|
+ InputBackendType::Reedline => "reedline",
|
|
+ InputBackendType::Basic => "basic",
|
|
+ InputBackendType::Minimal => "minimal",
|
|
+ },
|
|
+ std::io::IsTerminal::is_terminal(&std::io::stdin()),
|
|
+ will_run_interactively(&args),
|
|
+ ));
|
|
|
|
// Build UI options by merging config file with CLI args.
|
|
#[allow(unused_variables, reason = "not used when no backend features enabled")]
|
|
@@ -623,7 +660,18 @@ const fn new_error_behavior(args: &CommandLineArgs) -> error_formatter::Formatte
|
|
}
|
|
|
|
fn get_default_input_backend_type(args: &CommandLineArgs) -> InputBackendType {
|
|
- #[cfg(any(unix, windows))]
|
|
+ // On Redox, reedline's crossterm raw-mode terminal handling does not yet
|
|
+ // work over the fbcon console scheme: crossterm's raw-mode event poll never
|
|
+ // receives key events, so the interactive shell hangs without reading input.
|
|
+ // Plain line-based stdin reads DO work over fbcon (getty/login read the
|
|
+ // username that way), so use the minimal backend, which reads stdin
|
|
+ // directly. Remove this once reedline/crossterm raw mode is supported.
|
|
+ #[cfg(target_os = "redox")]
|
|
+ {
|
|
+ let _args = args;
|
|
+ InputBackendType::Minimal
|
|
+ }
|
|
+ #[cfg(all(any(unix, windows), not(target_os = "redox")))]
|
|
{
|
|
// If stdin isn't a terminal, then `reedline` doesn't do the right thing
|
|
// (reference: https://github.com/nushell/reedline/issues/509). Switch to
|