rust_crontab/
crontab.rs

1use crate::cronjob::CronJob;
2use std::{
3    process::Command,
4    str,
5    sync::{Arc, Mutex},
6};
7
8pub struct Crontab {
9    jobs: Vec<CronJob>,
10    lines: Vec<String>,
11}
12
13impl Crontab {
14    // 获取单例实例的方法
15
16    pub fn get_instance() -> Arc<Mutex<Crontab>> {
17        static mut INSTANCE: Option<Arc<Mutex<Crontab>>> = None;
18        unsafe {
19            INSTANCE
20                .get_or_insert_with(|| {
21                    Arc::new(Mutex::new(Crontab {
22                        jobs: vec![],
23                        lines: vec![],
24                    }))
25                })
26                .clone()
27        }
28    }
29}
30
31impl Crontab {
32    pub fn load(&mut self) -> Result<Vec<CronJob>, String> {
33        // Execute the `crontab -l` command
34        let output = Command::new("crontab")
35            .arg("-l")
36            .output()
37            .map_err(|err| format!("Failed to execute crontab command: {}", err))?;
38
39        // Check if the command was successful
40        if output.status.success() {
41            // Convert the output to a string and split it into lines (jobs)
42            let stdout = str::from_utf8(&output.stdout)
43                .map_err(|err| format!("Failed to parse crontab output: {}", err))?;
44
45            // Split lines, filter out comments/empty lines, and collect into a Vec<String>
46            let lines: Vec<String> = stdout
47                .lines()
48                .filter(|line| !line.trim().is_empty())
49                .map(|line| line.to_string())
50                .collect();
51            self.lines = lines.clone();
52            let mut jobs: Vec<CronJob> = vec![];
53            for line in lines {
54                jobs.push(CronJob::new(line));
55            }
56            self.jobs = jobs.clone();
57            Ok(jobs)
58        } else {
59            // Capture error output if the command fails
60            let stderr = str::from_utf8(&output.stderr)
61                .map_err(|err| format!("Failed to read error message: {}", err))?;
62            Err(stderr.to_string())
63        }
64    }
65
66    pub fn save(&mut self, jobs: Vec<CronJob>) -> Result<(), String> {
67        // Convert the jobs into a Vec<String>
68        let lines: Vec<String> = jobs.iter().map(|job| job.to_string()).collect();
69        self.lines = lines.clone();
70        // Join the lines into a single string
71        let lines = lines.join("\n");
72        // Execute the crontab command to update the crontab with the new jobs
73        let mut child = std::process::Command::new("crontab")
74            .stdin(std::process::Stdio::piped())
75            .spawn()
76            .map_err(|err| format!("Failed to execute crontab command: {}", err))?;
77
78        // Write the jobs (as string) to crontab's stdin
79        if let Some(mut stdin) = child.stdin.take() {
80            use std::io::Write;
81            stdin
82                .write_all(lines.as_bytes())
83                .map_err(|err| format!("Failed to write to crontab stdin: {}", err))?;
84        }
85
86        // Wait for the crontab command to finish
87        let output = child
88            .wait()
89            .map_err(|err| format!("Failed to wait on crontab process: {}", err))?;
90
91        if output.success() {
92            Ok(())
93        } else {
94            Err("Failed to update crontab".to_string())
95        }
96    }
97}