|
4 | 4 |
|
5 | 5 | use eframe::egui::{self, Color32, Pos2, RichText, Vec2, Window};
|
6 | 6 | use tokio::runtime::Runtime;
|
7 |
| -use std::process::Stdio; |
| 7 | +use std::process::{Stdio, Command, Child}; |
8 | 8 | use std::sync::{Arc, Mutex, RwLock};
|
9 | 9 | use std::thread;
|
10 | 10 | use tokio::time::Duration;
|
11 | 11 | use rfd::FileDialog;
|
12 | 12 | use autct::autctactions::{request_audit_verify, request_echo};
|
13 | 13 | use autct::config::AutctConfig;
|
| 14 | +use std::io; |
| 15 | + |
| 16 | +struct ProcessGuard { |
| 17 | + child: Child, |
| 18 | +} |
| 19 | + |
| 20 | +impl ProcessGuard { |
| 21 | + fn new(command: &str, args: &[&str]) -> io::Result<Self> { |
| 22 | + let child = Command::new(command) |
| 23 | + .args(args) |
| 24 | + .stderr(Stdio::null()) |
| 25 | + .stdout(Stdio::null()) |
| 26 | + .spawn()?; |
| 27 | + Ok(Self { child }) |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +// kills subprocess (here autct) on shutdown. |
| 32 | +// while this can cause substantial delays, we otherwise |
| 33 | +// would need to sync a previously running process |
| 34 | +// with current config, which is a bunch more mess. |
| 35 | +impl Drop for ProcessGuard { |
| 36 | + fn drop(&mut self) { |
| 37 | + println!("drop is getting called now"); |
| 38 | + // TODO is it even possible to |
| 39 | + // handle the case where kill fails to kill? |
| 40 | + let _ = self.child.kill(); |
| 41 | + } |
| 42 | +} |
| 43 | + |
14 | 44 |
|
15 | 45 | struct Wrapper {
|
16 | 46 | wrapped_app: Arc<RwLock<AutctVerifierApp>>,
|
@@ -40,6 +70,7 @@ struct AutctVerifierApp {
|
40 | 70 | last_verif_result: Option<String>,
|
41 | 71 | verif_runtime: Arc<Runtime>,
|
42 | 72 | server_state: Arc<Mutex<ServerState>>,
|
| 73 | + autct_process_guard: Option<ProcessGuard>, |
43 | 74 | }
|
44 | 75 |
|
45 | 76 | #[derive(Debug)]
|
@@ -71,18 +102,20 @@ async fn main() -> eframe::Result {
|
71 | 102 | // println-s here for now to sanity check the monitoring echo RPC
|
72 | 103 | // calls are behaving as expected:
|
73 | 104 | if !app_for_reading.autctcfg.is_none() {
|
74 |
| - println!("autctcfg was not none in the monitoring thread"); |
| 105 | + //println!("autctcfg was not none in the monitoring thread"); |
75 | 106 | let rt = Runtime::new().unwrap();
|
76 | 107 | let res = rt.block_on(
|
77 | 108 | request_echo(&app_for_reading.autctcfg.as_ref().unwrap()));
|
78 |
| - println!("Request echo method returned"); |
| 109 | + //println!("Request echo method returned"); |
79 | 110 | match res {
|
80 | 111 | Ok(_) => {println!("Echo successful");
|
81 | 112 | let mut state = app_for_reading
|
82 | 113 | .server_state.lock().unwrap();
|
83 | 114 | *state = ServerState::Ready;
|
84 | 115 | break;},
|
85 |
| - Err(_) => {println!("Echo call returned an error");} |
| 116 | + Err(_) => { |
| 117 | + //println!("Echo call returned an error"); |
| 118 | + } |
86 | 119 | } // end match
|
87 | 120 | } // end autctcfg none check
|
88 | 121 | else {
|
@@ -112,20 +145,25 @@ impl AutctVerifierApp {
|
112 | 145 | }
|
113 | 146 | }
|
114 | 147 |
|
115 |
| - fn check_cfg_data(&self) -> Result<AutctConfig, CustomError> { |
116 |
| - println!("Starting check cfg data"); |
| 148 | + fn check_cfg_data(&self, servermode: bool) -> Result<AutctConfig, CustomError> { |
| 149 | + // If servermode is true, we only care about the keyset |
| 150 | + // and network settings. Otherwise, we check all. |
117 | 151 | let mut cfg = self.autctcfg
|
118 | 152 | .clone().ok_or_else(|| CustomError("Failed to load config".to_string()))?;
|
| 153 | + if !servermode { |
119 | 154 | cfg.proof_file_str = Some(self.proof_file
|
120 | 155 | .lock().unwrap().clone().ok_or_else(
|
121 | 156 | || CustomError("Failed to load proof file location".to_string()))?);
|
| 157 | + } |
122 | 158 | let keyset_str = self.keyset_file
|
123 | 159 | .lock().unwrap().clone().ok_or_else(
|
124 | 160 | || CustomError("Failed to load keyset file location".to_string()))?;
|
125 | 161 | cfg.keysets = Some("mycontext".to_owned() + ":" + &keyset_str);
|
| 162 | + if !servermode { |
126 | 163 | cfg.user_string = Some(self.sigmessage
|
127 | 164 | .clone().ok_or_else(
|
128 | 165 | || CustomError("Failed to load signature message".to_string()))?);
|
| 166 | + } |
129 | 167 | cfg.bc_network = Some(self.network.clone());
|
130 | 168 | println!("Finishing check cfg data ok");
|
131 | 169 | Ok(cfg)
|
@@ -153,32 +191,36 @@ impl AutctVerifierApp {
|
153 | 191 | }
|
154 | 192 |
|
155 | 193 | /// Starts the verification server in the background
|
156 |
| - /// by calling std::process:Command |
157 |
| - fn start_server(&self) { |
| 194 | + /// by calling std::process:Command; we use a wrapper |
| 195 | + /// `ProcessGuard` object so that the background process |
| 196 | + /// can be automatically killed on drop. |
| 197 | + fn start_server(&mut self) { |
158 | 198 | let server_state = Arc::clone(
|
159 | 199 | &self.server_state);
|
160 |
| - let keyset = self.autctcfg.clone().unwrap().keysets.unwrap(); |
161 |
| - let network = self.autctcfg.clone().unwrap().bc_network.unwrap(); |
162 |
| - // Spawn a new thread to run the process |
163 |
| - thread::spawn(move || { |
164 |
| - |
165 |
| - let mut state = server_state.lock().unwrap(); |
166 |
| - *state = ServerState::NotStarted; |
167 |
| - |
168 |
| - |
169 |
| - // Expected to take a while |
170 |
| - // TODO: expect just crashes; ideally want to show a prompt |
171 |
| - // that server startup failed with advice on how to fix it: |
172 |
| - std::process::Command::new( |
173 |
| - "./autct").arg("-k").arg(keyset).arg("-n").arg( |
174 |
| - network) |
175 |
| - .stderr(Stdio::null()) |
176 |
| - .stdout(Stdio::null()) |
177 |
| - .spawn().expect("Failed to start autct process in background."); |
| 200 | + // read in the current config settings in the UI before |
| 201 | + // passing them as arguments to the autct server. |
| 202 | + let newcfg = self.check_cfg_data( |
| 203 | + true); |
| 204 | + if newcfg.is_err() { |
| 205 | + self.show_verif_modal = true; |
| 206 | + } else { |
| 207 | + self.autctcfg = Some(newcfg.unwrap()); |
| 208 | + let keyset = self.autctcfg.clone().unwrap().keysets.unwrap(); |
| 209 | + let network = self.autctcfg.clone().unwrap().bc_network.unwrap(); |
| 210 | + println!("Starting process with keyset: {}, network: {}", keyset, network); |
| 211 | + // TODO a failure to start is silent here; the user's |
| 212 | + // only feedback is that the dot won't turn green. |
| 213 | + self.autct_process_guard = Some(ProcessGuard::new( |
| 214 | + "./autct", &[ |
| 215 | + "-M", "serve", |
| 216 | + "-k", &keyset, |
| 217 | + "-n", &network]).expect( |
| 218 | + "Failed to start child autct process")); |
178 | 219 | // Running means the server is processing the data
|
179 | 220 | // but not yet serving on the port:
|
| 221 | + let mut state = server_state.lock().unwrap(); |
180 | 222 | *state = ServerState::Running;
|
181 |
| - }); |
| 223 | + } |
182 | 224 | }
|
183 | 225 |
|
184 | 226 | fn update_dot(&mut self, ctx: &egui::Context,
|
@@ -230,6 +272,7 @@ impl Default for AutctVerifierApp {
|
230 | 272 | last_verif_result: None,
|
231 | 273 | verif_runtime: Arc::new(Runtime::new().unwrap()),
|
232 | 274 | server_state: Arc::new(Mutex::new(ServerState::NotStarted)),
|
| 275 | + autct_process_guard: None, |
233 | 276 | }
|
234 | 277 | }
|
235 | 278 | }
|
@@ -268,7 +311,7 @@ impl eframe::App for Wrapper {
|
268 | 311 | egui::Button::new(RichText::new(
|
269 | 312 | "VERIFY").size(30.0).strong())).clicked() {
|
270 | 313 | let newcfg =
|
271 |
| - state.check_cfg_data(); |
| 314 | + state.check_cfg_data(false); |
272 | 315 | if newcfg.is_err() {
|
273 | 316 | state.show_verif_modal = true;
|
274 | 317 | } else {
|
|
0 commit comments