Remove two useless comments.
[stakingWatchdogWatchdog.git] / src / main.rs
1 use std::{
2 fmt,
3 net::UdpSocket,
4 thread,
5 time::{self, Duration},
6 };
7
8 use anyhow::Result;
9 use lettre::{
10 message::header::ContentType, transport::smtp::authentication::Credentials, Message,
11 SmtpTransport, Transport,
12 };
13 use rand::{rngs::ThreadRng, Rng};
14
15 use crate::config::Config;
16
17 mod config;
18
19 const FILE_CONF: &str = "config.ron";
20 const PING_PERIOD: Duration = Duration::from_secs(5); // 5 s.
21 const EMAIL_RESEND_PERIOD: Duration = Duration::from_secs(2 * 60 * 60); // 2 h.
22 const STATE_PRINT_PERIOD: Duration = Duration::from_secs(15 * 60); // 15 min.
23 const SOCKET_TIMEOUT: Duration = Duration::from_secs(7);
24
25 fn main() -> Result<()> {
26 println!("Staking Watchdog Watchdog");
27
28 let config = Config::read(FILE_CONF)?;
29
30 println!(
31 "Configuration: {:?}",
32 Config {
33 smtp_password: "*****".to_string(),
34 ..config.clone()
35 }
36 );
37
38 let mut time_last_email_send = time::Instant::now() - EMAIL_RESEND_PERIOD;
39 let mut time_last_state_printed = time::Instant::now() - STATE_PRINT_PERIOD;
40 let mut error_state = false;
41
42 let mut rng = rand::thread_rng();
43
44 let mut number_of_pings = 0;
45 let mut total_ping_duration = Duration::default();
46
47 let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
48 socket.connect("192.168.2.102:8739").unwrap();
49 socket.set_nonblocking(false).unwrap();
50 socket.set_read_timeout(Some(SOCKET_TIMEOUT)).unwrap();
51 socket.set_write_timeout(Some(SOCKET_TIMEOUT)).unwrap();
52
53 loop {
54 let time_beginning_loop = time::Instant::now();
55
56 match ping(&socket, &mut rng) {
57 Ok(t) => {
58 total_ping_duration += t;
59 number_of_pings += 1;
60
61 if error_state {
62 error_state = false;
63 println!("End of erroneous state");
64 }
65
66 if time::Instant::now() - time_last_state_printed >= STATE_PRINT_PERIOD {
67 println!(
68 "No error detected. Mean of ping time: {} μs",
69 total_ping_duration.as_micros() / number_of_pings
70 );
71 total_ping_duration = Duration::default();
72 number_of_pings = 0;
73 time_last_state_printed = time::Instant::now();
74 }
75 }
76 Err(error) => {
77 error_state = true;
78 println!("Error: {:?}", error);
79 if time::Instant::now() - time_last_email_send >= EMAIL_RESEND_PERIOD {
80 // Send e-mail.
81 println!("Sending email...");
82 match send_email(
83 "Watchdog Watchdog: Check alive error",
84 &format!("Error: {}", error),
85 &config.smtp_relay_address,
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 SocketReceiveError(std::io::Error),
111 SocketSendError(std::io::Error),
112 WrongMessageReceived(String),
113 }
114
115 impl fmt::Display for PingError {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 match self {
118 PingError::SocketReceiveError(error) => {
119 write!(f, "Didn't receive any response from watchdog: {}", error)
120 }
121 PingError::SocketSendError(error) => {
122 write!(f, "Unable to send the message: {}", error)
123 }
124 PingError::WrongMessageReceived(message) => {
125 write!(f, "Watchdog replay with a wrong message: {}", message)
126 }
127 }
128 }
129 }
130
131 fn ping(socket: &UdpSocket, rng: &mut ThreadRng) -> std::result::Result<Duration, PingError> {
132 loop {
133 let number: u64 = rng.gen();
134 let mut buffer = number.to_le_bytes();
135 let now = time::Instant::now();
136 match socket.send(&buffer) {
137 Ok(_size_sent) => {
138 buffer.fill(0);
139 match socket.recv(&mut buffer) {
140 Ok(size_received) => {
141 if size_received == 8 {
142 let number_received = u64::from_le_bytes(buffer);
143 if number_received == number {
144 return Ok(time::Instant::now() - now);
145 } else {
146 return Err(PingError::WrongMessageReceived(format!(
147 "Message number receceived ({}) is not equal to the one sent ({})",
148 number_received, number
149 )));
150 }
151 } else {
152 return Err(PingError::WrongMessageReceived(format!(
153 "Size of packet must be 8, received size: {}",
154 size_received
155 )));
156 }
157 }
158 // FIXME.
159 // Test the kind because sometime 'recv' returns
160 // '(Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }'.
161 // Try again in this case.
162 Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
163 println!("WouldBlock error: {}", error)
164 }
165 Err(error) => return Err(PingError::SocketReceiveError(error)),
166 }
167 }
168 Err(error) => return Err(PingError::SocketSendError(error)),
169 }
170 }
171 }
172
173 fn send_email(
174 title: &str,
175 body: &str,
176 smtp_relay_address: &str,
177 login: &str,
178 pass: &str,
179 ) -> Result<()> {
180 let email = Message::builder()
181 .message_id(None)
182 .from("Staking Watchdog Watchdog <redmine@d-lan.net>".parse()?)
183 .to("Greg Burri <greg.burri@gmail.com>".parse()?)
184 .subject(title)
185 .header(ContentType::TEXT_PLAIN)
186 .body(body.to_string())?;
187
188 let creds = Credentials::new(login.to_string(), pass.to_string());
189
190 let mailer = SmtpTransport::relay(smtp_relay_address)?
191 .credentials(creds)
192 .build();
193
194 let response = mailer.send(&email)?;
195
196 println!("{:?}", response);
197
198 Ok(())
199 }