Remove two useless comments.
[stakingWatchdogWatchdog.git] / src / main.rs
index b51a6a8..9944ae1 100644 (file)
@@ -1,4 +1,5 @@
 use std::{
+    fmt,
     net::UdpSocket,
     thread,
     time::{self, Duration},
@@ -19,6 +20,7 @@ const FILE_CONF: &str = "config.ron";
 const PING_PERIOD: Duration = Duration::from_secs(5); // 5 s.
 const EMAIL_RESEND_PERIOD: Duration = Duration::from_secs(2 * 60 * 60); // 2 h.
 const STATE_PRINT_PERIOD: Duration = Duration::from_secs(15 * 60); // 15 min.
+const SOCKET_TIMEOUT: Duration = Duration::from_secs(7);
 
 fn main() -> Result<()> {
     println!("Staking Watchdog Watchdog");
@@ -39,46 +41,58 @@ fn main() -> Result<()> {
 
     let mut rng = rand::thread_rng();
 
+    let mut number_of_pings = 0;
+    let mut total_ping_duration = Duration::default();
+
     let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
     socket.connect("192.168.2.102:8739").unwrap();
-    socket
-        .set_read_timeout(Some(Duration::from_secs(5)))
-        .unwrap();
-    socket
-        .set_write_timeout(Some(Duration::from_secs(5)))
-        .unwrap();
+    socket.set_nonblocking(false).unwrap();
+    socket.set_read_timeout(Some(SOCKET_TIMEOUT)).unwrap();
+    socket.set_write_timeout(Some(SOCKET_TIMEOUT)).unwrap();
 
     loop {
         let time_beginning_loop = time::Instant::now();
 
-        if let Err(error) = ping(&socket, &mut rng) {
-            error_state = true;
-            println!("Error: {:?}", error);
-            if time::Instant::now() - time_last_email_send >= EMAIL_RESEND_PERIOD {
-                // Send e-mail.
-                println!("Sending email...");
-                match send_email(
-                    "Watchdog ERROR",
-                    &format!("Error: {:?}", error),
-                    &config.smtp_login,
-                    &config.smtp_password,
-                ) {
-                    Err(email_error) => println!("Error sending email: {:?}", email_error),
-                    _ => {
-                        println!("Email successfully sent");
-                        time_last_email_send = time::Instant::now();
-                    }
+        match ping(&socket, &mut rng) {
+            Ok(t) => {
+                total_ping_duration += t;
+                number_of_pings += 1;
+
+                if error_state {
+                    error_state = false;
+                    println!("End of erroneous state");
                 }
-            }
-        } else {
-            if error_state {
-                error_state = false;
-                println!("End of erroneous state");
-            }
 
-            if time::Instant::now() - time_last_state_printed >= STATE_PRINT_PERIOD {
-                println!("No error detected");
-                time_last_state_printed = time::Instant::now();
+                if time::Instant::now() - time_last_state_printed >= STATE_PRINT_PERIOD {
+                    println!(
+                        "No error detected. Mean of ping time: {} μs",
+                        total_ping_duration.as_micros() / number_of_pings
+                    );
+                    total_ping_duration = Duration::default();
+                    number_of_pings = 0;
+                    time_last_state_printed = time::Instant::now();
+                }
+            }
+            Err(error) => {
+                error_state = true;
+                println!("Error: {:?}", error);
+                if time::Instant::now() - time_last_email_send >= EMAIL_RESEND_PERIOD {
+                    // Send e-mail.
+                    println!("Sending email...");
+                    match send_email(
+                        "Watchdog Watchdog: Check alive error",
+                        &format!("Error: {}", error),
+                        &config.smtp_relay_address,
+                        &config.smtp_login,
+                        &config.smtp_password,
+                    ) {
+                        Err(email_error) => println!("Error sending email: {:?}", email_error),
+                        _ => {
+                            println!("Email successfully sent");
+                            time_last_email_send = time::Instant::now();
+                        }
+                    }
+                }
             }
         }
 
@@ -93,47 +107,79 @@ fn main() -> Result<()> {
 
 #[derive(Debug)]
 enum PingError {
-    SocketError(std::io::Error),
+    SocketReceiveError(std::io::Error),
+    SocketSendError(std::io::Error),
     WrongMessageReceived(String),
 }
 
-fn ping(socket: &UdpSocket, rng: &mut ThreadRng) -> std::result::Result<(), PingError> {
-    let number: u64 = rng.gen();
-    let mut buffer = number.to_le_bytes();
-
-    match socket.send(&buffer) {
-        Ok(_size_sent) => {
-            buffer.fill(0);
-            match socket.recv(&mut buffer) {
-                Ok(size_received) => {
-                    if size_received == 8 {
-                        let number_received = u64::from_le_bytes(buffer);
-                        if number_received != number {
+impl fmt::Display for PingError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            PingError::SocketReceiveError(error) => {
+                write!(f, "Didn't receive any response from watchdog: {}", error)
+            }
+            PingError::SocketSendError(error) => {
+                write!(f, "Unable to send the message: {}", error)
+            }
+            PingError::WrongMessageReceived(message) => {
+                write!(f, "Watchdog replay with a wrong message: {}", message)
+            }
+        }
+    }
+}
+
+fn ping(socket: &UdpSocket, rng: &mut ThreadRng) -> std::result::Result<Duration, PingError> {
+    loop {
+        let number: u64 = rng.gen();
+        let mut buffer = number.to_le_bytes();
+        let now = time::Instant::now();
+        match socket.send(&buffer) {
+            Ok(_size_sent) => {
+                buffer.fill(0);
+                match socket.recv(&mut buffer) {
+                    Ok(size_received) => {
+                        if size_received == 8 {
+                            let number_received = u64::from_le_bytes(buffer);
+                            if number_received == number {
+                                return Ok(time::Instant::now() - now);
+                            } else {
+                                return Err(PingError::WrongMessageReceived(format!(
+                                    "Message number receceived ({}) is not equal to the one sent ({})",
+                                    number_received, number
+                                )));
+                            }
+                        } else {
                             return Err(PingError::WrongMessageReceived(format!(
-                                "Message number receceived ({}) is not equal to the one sent ({})",
-                                number_received, number
+                                "Size of packet must be 8, received size: {}",
+                                size_received
                             )));
                         }
-                    } else {
-                        return Err(PingError::WrongMessageReceived(format!(
-                            "Size of packet must be 8, received size: {}",
-                            size_received
-                        )));
                     }
+                    // FIXME.
+                    // Test the kind because sometime 'recv' returns
+                    // '(Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }'.
+                    // Try again in this case.
+                    Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
+                        println!("WouldBlock error: {}", error)
+                    }
+                    Err(error) => return Err(PingError::SocketReceiveError(error)),
                 }
-                Err(error) => return Err(PingError::SocketError(error)),
             }
+            Err(error) => return Err(PingError::SocketSendError(error)),
         }
-        Err(error) => return Err(PingError::SocketError(error)),
     }
-
-    Ok(())
 }
 
-fn send_email(title: &str, body: &str, login: &str, pass: &str) -> Result<()> {
+fn send_email(
+    title: &str,
+    body: &str,
+    smtp_relay_address: &str,
+    login: &str,
+    pass: &str,
+) -> Result<()> {
     let email = Message::builder()
         .message_id(None)
-        .from("Staking Watchdog <redmine@d-lan.net>".parse()?)
+        .from("Staking Watchdog Watchdog <redmine@d-lan.net>".parse()?)
         .to("Greg Burri <greg.burri@gmail.com>".parse()?)
         .subject(title)
         .header(ContentType::TEXT_PLAIN)
@@ -141,12 +187,10 @@ fn send_email(title: &str, body: &str, login: &str, pass: &str) -> Result<()> {
 
     let creds = Credentials::new(login.to_string(), pass.to_string());
 
-    // Open a remote connection to gmail
-    let mailer = SmtpTransport::relay("mail.gandi.net")?
+    let mailer = SmtpTransport::relay(smtp_relay_address)?
         .credentials(creds)
         .build();
 
-    // Send the email
     let response = mailer.send(&email)?;
 
     println!("{:?}", response);