use std::{
+ fmt,
net::UdpSocket,
thread,
time::{self, Duration},
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");
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();
+ }
+ }
+ }
}
}
#[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)
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);