Skip to content

Commit 09d4342

Browse files
committed
fix extra logging, make sure application-wide commands such as start are
more likely to succeed, improve indications of something closing in webUI
1 parent fe431fd commit 09d4342

File tree

5 files changed

+157
-60
lines changed

5 files changed

+157
-60
lines changed

src/AppState.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct AppState {
1414
pub config: Arc<Mutex<Config>>,
1515
pub slave_servers: Arc<Mutex<Vec<ServerInfo>>>,
1616
pub slave_connections: Arc<Mutex<Vec<SlaveConnection>>>,
17+
pub global_crash_prevention: Arc<AtomicBool>,
1718
}
1819
impl AppState {
1920
pub fn new(tx: broadcast::Sender<String>, config: Config) -> Self {
@@ -24,6 +25,7 @@ impl AppState {
2425
config: Arc::new(Mutex::new(config)),
2526
slave_servers: Arc::new(Mutex::new(vec![])),
2627
slave_connections: Arc::new(Mutex::new(vec![])),
28+
global_crash_prevention: Arc::new(AtomicBool::new(true)),
2729
}
2830
}
2931
pub fn stop(&mut self) {

src/ControlledProgram.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub struct ControlledProgramDescriptor {
2727
pub arguments: Vec<String>,
2828
pub working_dir: String,
2929
pub auto_start: bool,
30+
pub crash_prevention: bool,
3031
//optional, do not use unless you need specialization, remove if unused then fix errors by removing lines
3132
pub specialized_server_type: Option<SpecializedServerTypes>,
3233
pub specialized_server_info: Option<SpecializedServerInformation>,
@@ -46,6 +47,7 @@ impl ControlledProgramDescriptor {
4647
arguments,
4748
working_dir,
4849
auto_start,
50+
crash_prevention: true,
4951
specialized_server_type: None,
5052
specialized_server_info: None,
5153
}
@@ -57,6 +59,7 @@ impl ControlledProgramDescriptor {
5759
arguments,
5860
working_dir,
5961
auto_start: false,
62+
crash_prevention: true,
6063
specialized_server_type: None,
6164
specialized_server_info: None,
6265
}
@@ -74,6 +77,7 @@ impl ControlledProgramDescriptor {
7477
instance.set_specialization(value);
7578
}
7679
}
80+
instance.crash_prevention = self.crash_prevention;
7781
instance
7882
}
7983
pub fn set_specialization(&mut self, spec: SpecializedServerTypes) {
@@ -88,6 +92,7 @@ impl Default for ControlledProgramDescriptor {
8892
arguments: vec![],
8993
working_dir: "".to_owned(),
9094
auto_start: false,
95+
crash_prevention: true,
9196
specialized_server_type: None,
9297
specialized_server_info: None,
9398
}
@@ -104,6 +109,7 @@ pub struct ControlledProgramInstance {
104109
#[allow(unused)]
105110
pub last_log_lines: usize,
106111
pub curr_output_in_progress: String,
112+
pub crash_prevention: bool,
107113
//optional, remove if unused then remove any references within this file
108114
pub specialized_server_type: Option<SpecializedServerTypes>,
109115
pub specialized_server_info: Option<SpecializedServerInformation>,
@@ -137,6 +143,7 @@ impl ControlledProgramInstance {
137143
working_dir,
138144
last_log_lines: 0,
139145
curr_output_in_progress: "".to_string(),
146+
crash_prevention: true,
140147
specialized_server_type: None,
141148
specialized_server_info: None,
142149
}
@@ -333,7 +340,16 @@ impl ControlledProgramInstance {
333340
None => None,
334341
}
335342
}
336-
pub async fn stop(&mut self) {
343+
pub async fn stop(&mut self) -> Option<i32> {
344+
// Disable crash prevention so the process won't be restarted when killed
345+
self.crash_prevention = false;
337346
let _ = self.process.kill().await;
347+
348+
// Wait a moment for the process to terminate and get the exit code
349+
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
350+
match self.process.try_wait() {
351+
Ok(Some(status)) => status.code(),
352+
_ => None,
353+
}
338354
}
339355
}

src/html_src/index.js

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -118,70 +118,72 @@ function addServerDropdown(serverName, inactive) {
118118

119119
var numBack = 0;
120120
var input = dropdown.find(".STDInInput");
121+
122+
// Create a helper function to handle the console input consistently
123+
function handleConsoleInput(inputValue) {
124+
if (inputValue == "") return;
125+
126+
// Normalize start command - check if it's a start command (case insensitive and ignoring whitespace)
127+
var isStartCommand = inputValue.trim().toLowerCase() === "start";
128+
129+
var obj = {
130+
type: "stdinInput",
131+
server_name: serverName,
132+
value: isStartCommand ? "start" : inputValue,
133+
};
134+
socket.send(JSON.stringify(obj));
135+
136+
// Clear console output if starting an inactive server
137+
if (isStartCommand && dropdown.hasClass("inactiveServer")) {
138+
// Clear console output and add starting message
139+
$("." + serverName + "Out").empty();
140+
var startingMsg = $(
141+
'<p class="STDOutMessage" style="color: #FFFF00;">Starting server, please wait...</p>',
142+
);
143+
$("." + serverName + "Out").append(startingMsg);
144+
}
145+
146+
// Store command in history
147+
if (commands.length > 25) {
148+
var commandsTemp = [];
149+
for (
150+
var i = Math.max(0, commands.length - 25);
151+
i < commands.length;
152+
++i
153+
) {
154+
commandsTemp.push(commands[i]);
155+
}
156+
commands = commandsTemp;
157+
}
158+
commands.push(inputValue);
159+
numBack = 0;
160+
}
161+
162+
// Handle keyboard input
121163
input.keydown(function (e) {
122164
if (e.which === 13) {
123-
//enter pressed
124-
var input2 = $(this).val();
125-
if (input2 == "") return;
165+
// Enter key
166+
var inputValue = $(this).val();
126167
$(this).val("");
127-
var obj = {
128-
type: "stdinInput",
129-
server_name: serverName,
130-
value: input2,
131-
};
132-
133-
if (input2 == "start" && dropdow.hasClass("inactiveServer")) {
134-
$("." + serverName + "Out")
135-
.children()
136-
.remove();
137-
}
138-
if (commands.length > 25) {
139-
var commandsTemp = [];
140-
for (
141-
var i = Math.max(0, commands.length - 26);
142-
i < commands.length;
143-
++i
144-
) {
145-
commandsTemp.push(commands[i]);
146-
}
147-
commands = commandsTemp;
148-
}
149-
commands.push(input2);
150-
numBack = 0;
151-
socket.send(JSON.stringify(obj));
168+
handleConsoleInput(inputValue);
152169
} else if (e.which === 40) {
153-
//down arrow
170+
// Down arrow
154171
numBack = Math.max(0, numBack - 1);
155-
$(this).val(commands[commands.length - numBack]);
172+
$(this).val(numBack > 0 ? commands[commands.length - numBack] : "");
156173
} else if (e.which === 38) {
157-
//up arrow
174+
// Up arrow
158175
numBack = Math.min(commands.length, numBack + 1);
159176
$(this).val(commands[commands.length - numBack]);
160177
}
161178
});
162-
var commandsTemp = [];
179+
180+
// Handle submit button click
163181
dropdown.find(".STDInSubmit").click(function (e) {
164182
if (e.which === 1) {
165-
var input2 = $(input).val();
166-
if (input2 == "") return;
167-
$(input).val("");
168-
var obj = {
169-
type: "stdinInput",
170-
server_name: serverName,
171-
value: input2,
172-
};
173-
socket.send(JSON.stringify(obj));
174-
var commandsTemp = [];
175-
window.commands = commandsTemp;
176-
window.commands.push(input2);
177-
for (
178-
var i = Math.max(0, window.commands.length - 26);
179-
i < window.commands.length;
180-
++i
181-
) {
182-
commandsTemp.push(window.commands[i]);
183-
}
184-
numBack = 0;
183+
// Left mouse button
184+
var inputValue = input.val();
185+
input.val("");
186+
handleConsoleInput(inputValue);
185187
}
186188
});
187189
dropdown.find(".dropdownDrop").slideUp(1).hide();

src/servers.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@ use crate::{
22
messages::ConsoleOutput, AppState::AppState, ControlledProgram::ControlledProgramDescriptor,
33
};
44
use tracing::*;
5+
6+
// Helper function to send server termination message to web console
7+
pub async fn send_termination_message(
8+
state: &AppState,
9+
server_name: String,
10+
exit_code: i32,
11+
server_type: Option<crate::ControlledProgram::SpecializedServerTypes>,
12+
) {
13+
let termination_msg = ConsoleOutput {
14+
r#type: "ServerOutput".to_owned(),
15+
output: format!(
16+
"<span style=\"color: yellow;\">Program has stopped execution: StopCode: {}</span>",
17+
exit_code
18+
),
19+
server_name,
20+
server_type,
21+
};
22+
let _ = state
23+
.tx
24+
.send(serde_json::to_string(&termination_msg).unwrap());
25+
}
526
#[no_mangle]
627
pub async fn start_servers(_state: AppState) {
728
let mut config = _state.config.lock().await;
@@ -31,7 +52,15 @@ pub async fn process_stdout(state: AppState) {
3152
"A child process has closed! index: {} ExitCode: {}",
3253
index, exit_code
3354
);
34-
if exit_code != 0 {
55+
// Send termination message to web console
56+
send_termination_message(
57+
&state,
58+
server.name.clone(),
59+
exit_code,
60+
server.specialized_server_type.clone(),
61+
)
62+
.await;
63+
if exit_code != 0 && server.crash_prevention {
3564
info!("Server ID: {} has crashed, restarting it...", index);
3665
let mut descriptor = ControlledProgramDescriptor::new(
3766
server.name.as_str(),
@@ -44,7 +73,20 @@ pub async fn process_stdout(state: AppState) {
4473
server.specialized_server_type.clone().unwrap(),
4574
);
4675
}
76+
77+
// Lookup the original crash_prevention setting from config to preserve it
78+
let config = state.config.lock().await;
79+
for server_config in config.servers.iter() {
80+
if server_config.name == server.name {
81+
descriptor.crash_prevention = server_config.crash_prevention;
82+
break;
83+
}
84+
}
85+
drop(config);
86+
4787
new_instances.push(descriptor);
88+
} else if exit_code != 0 {
89+
info!("Server ID: {} has crashed, but crash prevention is disabled. Not restarting.", index);
4890
}
4991
to_remove.push(index);
5092
}

src/websocket.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tokio::io::AsyncWriteExt;
1111
use tracing::*;
1212

1313
use crate::{
14-
configuration::Config, messages::*, AppState::AppState,
14+
configuration::Config, messages::*, servers::send_termination_message, AppState::AppState,
1515
ControlledProgram::ControlledProgramDescriptor,
1616
};
1717
#[no_mangle]
@@ -89,9 +89,6 @@ async fn process_message(text: String, state: AppState) {
8989
.collect();
9090
}
9191
}
92-
if ev_type != "requestInfo" {
93-
info!("Message: {}", text.clone());
94-
}
9592
match ev_type {
9693
"requestInfo" => {
9794
let servers = state.servers.lock().await;
@@ -224,10 +221,39 @@ async fn process_message(text: String, state: AppState) {
224221
}
225222
}
226223
"terminateServers" => {
224+
let global_cp_value = state
225+
.global_crash_prevention
226+
.load(std::sync::atomic::Ordering::SeqCst);
227+
let need_global_cp = global_cp_value.clone();
228+
if global_cp_value == true {
229+
state
230+
.global_crash_prevention
231+
.store(false, std::sync::atomic::Ordering::SeqCst);
232+
}
227233
let mut servers = state.servers.lock().await;
234+
// First set all servers to disable crash prevention
235+
236+
// Then stop all servers
228237
for server in servers.iter_mut() {
229-
server.stop().await;
238+
if let Some(exit_code) = server.stop().await {
239+
// Send termination message to web console for each server
240+
send_termination_message(
241+
&state,
242+
server.name.clone(),
243+
exit_code,
244+
server.specialized_server_type.clone(),
245+
)
246+
.await;
247+
}
230248
}
249+
250+
if need_global_cp == true {
251+
state
252+
.global_crash_prevention
253+
.store(true, std::sync::atomic::Ordering::SeqCst);
254+
}
255+
servers.clear();
256+
drop(servers);
231257
}
232258
"configChange" => {
233259
#[allow(unused)]
@@ -241,7 +267,16 @@ async fn process_message(text: String, state: AppState) {
241267
let mut config = state.config.lock().await;
242268

243269
for server in servers.iter_mut() {
244-
server.stop().await
270+
if let Some(exit_code) = server.stop().await {
271+
// Send termination message to web console for each server
272+
send_termination_message(
273+
&state,
274+
server.name.clone(),
275+
exit_code,
276+
server.specialized_server_type.clone(),
277+
)
278+
.await;
279+
}
245280
}
246281
servers.clear();
247282
config.change(message.updated_config);

0 commit comments

Comments
 (0)