]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/sync.rs
tfa: remember recovery indices
[proxmox-backup.git] / src / config / sync.rs
CommitLineData
3eeba687 1use anyhow::{Error};
f486e9e5 2use lazy_static::lazy_static;
b4900286
DM
3use std::collections::HashMap;
4use serde::{Serialize, Deserialize};
5
6use proxmox::api::{
7 api,
8 schema::*,
9 section_config::{
10 SectionConfig,
11 SectionConfigData,
12 SectionConfigPlugin,
13 }
14};
f486e9e5 15
b4900286 16use proxmox::tools::{fs::replace_file, fs::CreateOptions};
f486e9e5 17
b4900286 18use crate::api2::types::*;
f486e9e5 19
b4900286
DM
20lazy_static! {
21 static ref CONFIG: SectionConfig = init();
f486e9e5
DM
22}
23
b4900286
DM
24#[api(
25 properties: {
26 id: {
27 schema: JOB_ID_SCHEMA,
28 },
29 store: {
30 schema: DATASTORE_SCHEMA,
31 },
f1694b06
FG
32 "owner": {
33 type: Authid,
34 optional: true,
35 },
b4900286
DM
36 remote: {
37 schema: REMOTE_ID_SCHEMA,
38 },
39 "remote-store": {
40 schema: DATASTORE_SCHEMA,
41 },
42 "remove-vanished": {
43 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
44 optional: true,
45 },
46 comment: {
47 optional: true,
48 schema: SINGLE_LINE_COMMENT_SCHEMA,
49 },
50 schedule: {
51 optional: true,
2888b27f 52 schema: SYNC_SCHEDULE_SCHEMA,
b4900286
DM
53 },
54 }
55)]
56#[serde(rename_all="kebab-case")]
9e733dae 57#[derive(Serialize,Deserialize,Clone)]
6f652b1b
DM
58/// Sync Job
59pub struct SyncJobConfig {
b4900286
DM
60 pub id: String,
61 pub store: String,
f1694b06
FG
62 #[serde(skip_serializing_if="Option::is_none")]
63 pub owner: Option<Authid>,
b4900286
DM
64 pub remote: String,
65 pub remote_store: String,
66 #[serde(skip_serializing_if="Option::is_none")]
67 pub remove_vanished: Option<bool>,
68 #[serde(skip_serializing_if="Option::is_none")]
69 pub comment: Option<String>,
70 #[serde(skip_serializing_if="Option::is_none")]
71 pub schedule: Option<String>,
f486e9e5
DM
72}
73
59af9ca9
FG
74impl From<&SyncJobStatus> for SyncJobConfig {
75 fn from(job_status: &SyncJobStatus) -> Self {
76 Self {
77 id: job_status.id.clone(),
78 store: job_status.store.clone(),
79 owner: job_status.owner.clone(),
80 remote: job_status.remote.clone(),
81 remote_store: job_status.remote_store.clone(),
82 remove_vanished: job_status.remove_vanished.clone(),
83 comment: job_status.comment.clone(),
84 schedule: job_status.schedule.clone(),
85 }
86 }
87}
88
997d7e19
DC
89// FIXME: generate duplicate schemas/structs from one listing?
90#[api(
91 properties: {
92 id: {
93 schema: JOB_ID_SCHEMA,
94 },
95 store: {
96 schema: DATASTORE_SCHEMA,
97 },
f1694b06
FG
98 owner: {
99 type: Authid,
100 optional: true,
101 },
997d7e19
DC
102 remote: {
103 schema: REMOTE_ID_SCHEMA,
104 },
105 "remote-store": {
106 schema: DATASTORE_SCHEMA,
107 },
108 "remove-vanished": {
109 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
110 optional: true,
111 },
112 comment: {
113 optional: true,
114 schema: SINGLE_LINE_COMMENT_SCHEMA,
115 },
116 schedule: {
117 optional: true,
118 schema: SYNC_SCHEDULE_SCHEMA,
119 },
120 "next-run": {
121 description: "Estimated time of the next run (UNIX epoch).",
122 optional: true,
123 type: Integer,
124 },
125 "last-run-state": {
126 description: "Result of the last run.",
127 optional: true,
128 type: String,
129 },
130 "last-run-upid": {
131 description: "Task UPID of the last run.",
132 optional: true,
133 type: String,
134 },
135 "last-run-endtime": {
136 description: "Endtime of the last run.",
137 optional: true,
138 type: Integer,
139 },
140 }
141)]
142#[serde(rename_all="kebab-case")]
143#[derive(Serialize,Deserialize)]
144/// Status of Sync Job
145pub struct SyncJobStatus {
146 pub id: String,
147 pub store: String,
f1694b06
FG
148 #[serde(skip_serializing_if="Option::is_none")]
149 pub owner: Option<Authid>,
997d7e19
DC
150 pub remote: String,
151 pub remote_store: String,
152 #[serde(skip_serializing_if="Option::is_none")]
153 pub remove_vanished: Option<bool>,
154 #[serde(skip_serializing_if="Option::is_none")]
155 pub comment: Option<String>,
156 #[serde(skip_serializing_if="Option::is_none")]
157 pub schedule: Option<String>,
158 #[serde(skip_serializing_if="Option::is_none")]
159 pub next_run: Option<i64>,
160 #[serde(skip_serializing_if="Option::is_none")]
161 pub last_run_state: Option<String>,
162 #[serde(skip_serializing_if="Option::is_none")]
163 pub last_run_upid: Option<String>,
164 #[serde(skip_serializing_if="Option::is_none")]
165 pub last_run_endtime: Option<i64>,
166}
167
b4900286 168fn init() -> SectionConfig {
6f652b1b 169 let obj_schema = match SyncJobConfig::API_SCHEMA {
b4900286
DM
170 Schema::Object(ref obj_schema) => obj_schema,
171 _ => unreachable!(),
172 };
f486e9e5 173
6f652b1b 174 let plugin = SectionConfigPlugin::new("sync".to_string(), Some(String::from("id")), obj_schema);
b4900286
DM
175 let mut config = SectionConfig::new(&JOB_ID_SCHEMA);
176 config.register_plugin(plugin);
f486e9e5 177
b4900286
DM
178 config
179}
180
6f652b1b
DM
181pub const SYNC_CFG_FILENAME: &str = "/etc/proxmox-backup/sync.cfg";
182pub const SYNC_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.sync.lck";
b4900286
DM
183
184pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
3eeba687
DM
185
186 let content = proxmox::tools::fs::file_read_optional_string(SYNC_CFG_FILENAME)?;
187 let content = content.unwrap_or(String::from(""));
f486e9e5 188
b4900286 189 let digest = openssl::sha::sha256(content.as_bytes());
6f652b1b 190 let data = CONFIG.parse(SYNC_CFG_FILENAME, &content)?;
b4900286 191 Ok((data, digest))
f486e9e5
DM
192}
193
b4900286 194pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
6f652b1b 195 let raw = CONFIG.write(SYNC_CFG_FILENAME, &config)?;
f486e9e5 196
b4900286
DM
197 let backup_user = crate::backup::backup_user()?;
198 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
199 // set the correct owner/group/permissions while saving file
200 // owner(rw) = root, group(r)= backup
201 let options = CreateOptions::new()
202 .perm(mode)
203 .owner(nix::unistd::ROOT)
204 .group(backup_user.gid);
f486e9e5 205
6f652b1b 206 replace_file(SYNC_CFG_FILENAME, raw.as_bytes(), options)?;
f486e9e5 207
b4900286 208 Ok(())
f486e9e5
DM
209}
210
b4900286 211// shell completion helper
6f652b1b 212pub fn complete_sync_job_id(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
b4900286
DM
213 match config() {
214 Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
215 Err(_) => return vec![],
f486e9e5 216 }
f486e9e5 217}