Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions crates/rustisk-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1746,7 +1746,7 @@ fn load_pjsip_notify_config(content: &str, config: &asterisk_sip::notify::Notify
}

/// Perform the startup sequence.
async fn startup_sequence(config_dir: &str, dirs: &AsteriskDirs) {
async fn startup_sequence(config_dir: &str, dirs: &AsteriskDirs) -> Result<(), String> {
info!("Loading configuration from: {}", config_dir);

// Create module stubs (item 4)
Expand Down Expand Up @@ -2039,10 +2039,10 @@ async fn startup_sequence(config_dir: &str, dirs: &AsteriskDirs) {
}
}
Err(e) => {
warn!(
"Failed to start SIP stack on {}: {} (continuing without SIP)",
return Err(format!(
"Failed to start SIP stack on {}: {}",
bind_addr, e
);
));
}
}
}
Expand Down Expand Up @@ -2101,6 +2101,7 @@ async fn startup_sequence(config_dir: &str, dirs: &AsteriskDirs) {
asterisk_ami::publish_event(
asterisk_ami::AmiEvent::new("FullyBooted", 0x01).with_header("Status", "Fully Booted"),
);
Ok(())
}

/// Run the interactive CLI console using rustyline.
Expand Down Expand Up @@ -2331,7 +2332,11 @@ async fn main() {
}

// Run the full startup sequence (config, codecs, SIP, AMI, etc.)
startup_sequence(&config_dir, &dirs).await;
if let Err(e) = startup_sequence(&config_dir, &dirs).await {
error!("Startup failed: {}", e);
shutdown_sequence(&run_dir);
std::process::exit(1);
}

// Signal that the daemon is fully booted. Any pending
// `core waitfullybooted` connections will now unblock.
Expand Down
31 changes: 31 additions & 0 deletions crates/rustisk-cli/tests/startup_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,37 @@ fn foreground_mode_without_console_waits_for_shutdown_signal() {
);
}

#[test]
fn sip_bind_failure_exits_without_becoming_fully_booted() {
let _guard = lock_cli_tests();
let fixture = Fixture::new();
let occupied = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
let occupied_addr = occupied.local_addr().unwrap();
fs::write(
fixture.config_file.parent().unwrap().join("pjsip.conf"),
format!(
"[transport-udp]\ntype=transport\nprotocol=udp\nbind={}\n",
occupied_addr
),
)
.unwrap();

let output = run_asterisk(&fixture, &["-f"], None, Duration::from_secs(10));

assert!(!output.timed_out, "{}", output.combined());
assert!(!output.status.success(), "{}", output.combined());
assert!(
output.combined().contains("Failed to start SIP stack"),
"{}",
output.combined()
);
assert!(
!output.combined().contains("Rustisk is fully booted"),
"{}",
output.combined()
);
}

#[test]
fn always_fork_flag_does_not_enter_console_mode() {
let _guard = lock_cli_tests();
Expand Down
Loading