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