smb_server_test_dashboard/
main.rs1mod csv;
8mod http;
9mod json;
10
11use std::net::TcpListener;
12use std::path::PathBuf;
13use std::sync::Arc;
14use std::thread;
15
16const DEFAULT_SMB_PORT: u16 = 4450;
18const EXIT_USAGE: i32 = 2;
20
21struct Config {
23 port: u16,
24 csv_path: PathBuf,
25}
26
27fn main() {
28 let config = parse_args();
29 serve(config);
30}
31
32fn parse_args() -> Config {
35 let mut smb_port: u16 = DEFAULT_SMB_PORT;
36 let mut explicit_port: Option<u16> = None;
37 let mut csv_path = PathBuf::from("test_status.csv");
38
39 let mut args = std::env::args().skip(1);
40 while let Some(arg) = args.next() {
41 match arg.as_str() {
42 "--smb-port" => smb_port = next_u16(&mut args, "--smb-port"),
43 "--port" => explicit_port = Some(next_u16(&mut args, "--port")),
44 "--csv" => csv_path = PathBuf::from(next_value(&mut args, "--csv")),
45 "-h" | "--help" => print_help_and_exit(),
46 other => fail(&format!("unknown argument: {other}")),
47 }
48 }
49
50 Config { port: explicit_port.unwrap_or(smb_port + 1), csv_path }
51}
52
53fn next_value(args: &mut impl Iterator<Item = String>, flag: &str) -> String {
54 args.next().unwrap_or_else(|| fail(&format!("{flag} needs a value")))
55}
56
57fn next_u16(args: &mut impl Iterator<Item = String>, flag: &str) -> u16 {
58 next_value(args, flag)
59 .parse()
60 .unwrap_or_else(|_| fail(&format!("{flag} needs a number")))
61}
62
63fn print_help_and_exit() -> ! {
64 println!(
65 "smb-server-test-dashboard [--smb-port <n>] [--port <n>] [--csv <path>]\n\
66 \n\
67 Serves the MS-SMB2 test dashboard. Defaults to SMB port + 1 (4451) and\n\
68 reads/writes test_status.csv in the working directory."
69 );
70 std::process::exit(0);
71}
72
73fn fail(msg: &str) -> ! {
74 eprintln!("smb-server-test-dashboard: {msg}");
75 std::process::exit(EXIT_USAGE);
76}
77
78fn serve(config: Config) {
82 let addr = ("0.0.0.0", config.port);
83 let listener = TcpListener::bind(addr)
84 .unwrap_or_else(|e| fail(&format!("cannot bind port {}: {e}", config.port)));
85 let csv_path = Arc::new(config.csv_path);
86 println!(
87 "smb-server-test-dashboard listening on http://0.0.0.0:{} (csv: {})",
88 config.port,
89 csv_path.display()
90 );
91
92 for stream in listener.incoming() {
93 let Ok(stream) = stream else { continue };
94 let csv_path = Arc::clone(&csv_path);
95 thread::spawn(move || http::handle_connection(stream, &csv_path));
96 }
97}