Measure ping time.
[stakingWatchdogWatchdog.git] / src / main.rs
1 use std::{
2 net::UdpSocket,
3 thread,
4 time::{self, Duration},
5 };
6
7 use anyhow::Result;
8 use lettre::{
9 message::header::ContentType, transport::smtp::authentication::Credentials, Message,
10 SmtpTransport, Transport,
11 };
12 use rand::{rngs::ThreadRng, Rng};
13
14 use crate::config::Config;
15
16 mod config;
17
18 const FILE_CONF: &str = "config.ron";
19 const PING_PERIOD: Duration = Duration::from_secs(5); // 5 s.
20 const EMAIL_RESEND_PERIOD: Duration = Duration::from_secs(2 * 60 * 60); // 2 h.
21 const STATE_PRINT_PERIOD: Duration = Duration::from_secs(15 * 60); // 15 min.
22
23 fn main() -> Result<()> {
24 println!("Staking Watchdog Watchdog");
25
26 let config = Config::read(FILE_CONF)?;
27
28 println!(
29 "Configuration: {:?}",
30 Config {
31 smtp_password: "*****".to_string(),
32 ..config.clone()
33 }
34 );
35
36 let mut time_last_email_send = time::Instant::now() - EMAIL_RESEND_PERIOD;
37 let mut time_last_state_printed = time::Instant::now() - STATE_PRINT_PERIOD;
38 let mut error_state = false;
39
40 let mut rng = rand::thread_rng();
41
42 let mut number_of_pings = 0;
43 let mut total_ping_duration = Duration::default();
44
45 let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
46 socket.connect("192.168.2.102:8739").unwrap();
47 socket
48 .set_read_timeout(Some(Duration::from_secs(5)))
49 .unwrap();
50 socket
51 .set_write_timeout(Some(Duration::from_secs(5)))
52 .unwrap();
53
54 loop {
55 let time_beginning_loop = time::Instant::now();
56
57 match ping(&socket, &mut rng) {
58 Ok(t) => {
59 total_ping_duration += t;
60 number_of_pings += 1;
61
62 if error_state {
63 error_state = false;
64 println!("End of erroneous state");
65 }
66
67 if time::Instant::now() - time_last_state_printed >= STATE_PRINT_PERIOD {
68 println!(
69 "No error detected. Mean of ping time: {} μs",
70 total_ping_duration.as_micros() / number_of_pings
71 );
72 total_ping_duration = Duration::default();
73 number_of_pings = 0;
74 time_last_state_printed = time::Instant::now();
75 }
76 }
77 Err(error) => {
78 error_state = true;
79 println!("Error: {:?}", error);
80 if time::Instant::now() - time_last_email_send >= EMAIL_RESEND_PERIOD {
81 // Send e-mail.
82 println!("Sending email...");
83 match send_email(
84 "Watchdog ERROR",
85 &format!("Error: {:?}", error),
86 &config.smtp_login,
87 &config.smtp_password,
88 ) {
89 Err(email_error) => println!("Error sending email: {:?}", email_error),
90 _ => {
91 println!("Email successfully sent");
92 time_last_email_send = time::Instant::now();
93 }
94 }
95 }
96 }
97 }
98
99 let elapsed = time::Instant::now() - time_beginning_loop;
100
101 if elapsed < PING_PERIOD {
102 let to_wait = PING_PERIOD - elapsed;
103 thread::sleep(to_wait);
104 }
105 }
106 }
107
108 #[derive(Debug)]
109 enum PingError {
110 SocketError(std::io::Error),
111 WrongMessageReceived(String),
112 }
113
114 fn ping(socket: &UdpSocket, rng: &mut ThreadRng) -> std::result::Result<Duration, PingError> {
115 let number: u64 = rng.gen();
116 let mut buffer = number.to_le_bytes();
117
118 let now = time::Instant::now();
119 match socket.send(&buffer) {
120 Ok(_size_sent) => {
121 buffer.fill(0);
122 match socket.recv(&mut buffer) {
123 Ok(size_received) => {
124 if size_received == 8 {
125 let number_received = u64::from_le_bytes(buffer);
126 if number_received != number {
127 return Err(PingError::WrongMessageReceived(format!(
128 "Message number receceived ({}) is not equal to the one sent ({})",
129 number_received, number
130 )));
131 }
132 } else {
133 return Err(PingError::WrongMessageReceived(format!(
134 "Size of packet must be 8, received size: {}",
135 size_received
136 )));
137 }
138 }
139 Err(error) => return Err(PingError::SocketError(error)),
140 }
141 }
142 Err(error) => return Err(PingError::SocketError(error)),
143 }
144
145 Ok(time::Instant::now() - now)
146 }
147
148 fn send_email(title: &str, body: &str, login: &str, pass: &str) -> Result<()> {
149 let email = Message::builder()
150 .message_id(None)
151 .from("Staking Watchdog <redmine@d-lan.net>".parse()?)
152 .to("Greg Burri <greg.burri@gmail.com>".parse()?)
153 .subject(title)
154 .header(ContentType::TEXT_PLAIN)
155 .body(body.to_string())?;
156
157 let creds = Credentials::new(login.to_string(), pass.to_string());
158
159 // Open a remote connection to gmail
160 let mailer = SmtpTransport::relay("mail.gandi.net")?
161 .credentials(creds)
162 .build();
163
164 // Send the email
165 let response = mailer.send(&email)?;
166
167 println!("{:?}", response);
168
169 Ok(())
170 }