summaryrefslogtreecommitdiffstats
path: root/crates/core/tedge_agent/src/restart_operation_handler.rs
blob: ed1b46abdae95c21c6554b7467af0a66d229ae54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
pub mod restart_operation {

    use crate::error::AgentError;
    use std::{
        fs::File,
        fs::OpenOptions,
        io::Read,
        io::Write,
        path::{Path, PathBuf},
    };
    use time::OffsetDateTime;

    const SLASH_RUN_PATH_TEDGE_AGENT_RESTART: &str = "tedge_agent/tedge_agent_restart";
    const SLASH_PROC_UPTIME: &str = "/proc/uptime";

    /// creates an empty file in /run
    /// the file name defined by `SLASH_RUN_PATH_TEDGE_AGENT_RESTART`
    ///
    /// # Example
    /// ```
    /// let () = RestartOperationHelper::create_slash_run_file()?;
    /// ```
    pub fn create_slash_run_file(run_dir: &PathBuf) -> Result<(), AgentError> {
        let path = &run_dir.join(SLASH_RUN_PATH_TEDGE_AGENT_RESTART);
        let path = Path::new(path);

        let mut file = match OpenOptions::new()
            .create(true)
            .read(true)
            .write(true)
            .open(&path)
        {
            Ok(file) => file,
            Err(err) => {
                return Err(AgentError::FromIo(err));
            }
        };
        let date_utc = OffsetDateTime::now_utc().unix_timestamp();
        file.write_all(date_utc.to_string().as_bytes())?;
        Ok(())
    }

    pub fn slash_run_file_exists(run_dir: &PathBuf) -> bool {
        let path = &run_dir.join(SLASH_RUN_PATH_TEDGE_AGENT_RESTART);
        std::path::Path::new(path).exists()
    }

    /// returns the datetime of `SLASH_RUN_PATH_TEDGE_AGENT_RESTART` "modified at".
    fn get_restart_file_datetime(run_dir: &PathBuf) -> Result<time::OffsetDateTime, AgentError> {
        let mut file = File::open(&run_dir.join(SLASH_RUN_PATH_TEDGE_AGENT_RESTART))?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;

        let unix_timestamp = contents
            .parse::<i64>()
            .expect("Could not parse unix timestamp");

        let dt = match OffsetDateTime::from_unix_timestamp(unix_timestamp) {
            Ok(result) => result,
            Err(error) => {
                return Err(AgentError::TimestampConversionError {
                    timestamp: unix_timestamp,
                    error_msg: error.to_string(),
                });
            }
        };
        Ok(dt)
    }

    /// computes the time of last reboot.
    ///
    /// where "time of last reboot" is defined as:
    ///     current datetime - number of seconds the system has been up.
    ///
    /// number of seconds the system has been up are obtained from /proc/uptime
    fn get_system_uptime() -> Result<time::OffsetDateTime, AgentError> {
        // reading uptime
        let uptime_file = std::fs::File::open(std::path::Path::new(SLASH_PROC_UPTIME))?;
        let mut buf_reader = std::io::BufReader::new(uptime_file);
        let mut buffer = String::new();
        buf_reader.read_to_string(&mut buffer)?;

        // system uptime is the first value of the /proc/uptime file content
        let maybe_uptime = buffer.split(' ').next();

        if let Some(uptime) = maybe_uptime {
            match uptime.parse::<f64>() {
                Ok(result) => {
                    let local = OffsetDateTime::now_utc();
                    let uptime_secs = result as i64;
                    let duration = time::Duration::seconds(uptime_secs);
                    let reboot_time = local - duration;
                    Ok(reboot_time)
                }
                Err(_err) => Err(AgentError::FloatCastingError),
            }
        } else {
            Err(AgentError::UptimeParserError)
        }
    }

    /// checks if system rebooted by comparing dt of tedge_agent_restart with dt of system restart.
    pub fn has_rebooted(run_dir: &PathBuf) -> Result<bool, AgentError> {
        // there is no slash run file after the reboot, so we assume success.
        // this is true for most of the cases as "/run/" is normally cleared after a reboot.
        if !slash_run_file_exists(&run_dir) {
            return Ok(true);
        }

        let system_reboot_dt = get_system_uptime()?;
        let tedge_restart_file_dt = get_restart_file_datetime(&run_dir)?;

        Ok(system_reboot_dt > tedge_restart_file_dt)
    }
}