]> git.proxmox.com Git - proxmox-backup.git/blob - src/config/remotes.rs
src/api2/config: correctly lock files
[proxmox-backup.git] / src / config / remotes.rs
1 use failure::*;
2 use lazy_static::lazy_static;
3 use std::collections::HashMap;
4 use serde::{Serialize, Deserialize};
5
6 use proxmox::api::{api, schema::*};
7
8 use proxmox::tools::{fs::replace_file, fs::CreateOptions};
9
10 use crate::api2::types::*;
11 use crate::section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
12
13 lazy_static! {
14 static ref CONFIG: SectionConfig = init();
15 }
16
17 pub const REMOTE_PASSWORD_SCHEMA: Schema = StringSchema::new("Password or auth token for remote host.")
18 .format(&PASSWORD_FORMAT)
19 .min_length(1)
20 .max_length(1024)
21 .schema();
22
23 #[api(
24 properties: {
25 comment: {
26 optional: true,
27 schema: SINGLE_LINE_COMMENT_SCHEMA,
28 },
29 host: {
30 schema: DNS_NAME_OR_IP_SCHEMA,
31 },
32 userid: {
33 schema: PROXMOX_USER_ID_SCHEMA,
34 },
35 password: {
36 schema: REMOTE_PASSWORD_SCHEMA,
37 },
38 }
39 )]
40 #[derive(Serialize,Deserialize)]
41 /// Remote properties.
42 pub struct Remote {
43 #[serde(skip_serializing_if="Option::is_none")]
44 pub comment: Option<String>,
45 pub host: String,
46 pub userid: String,
47 pub password: String,
48 }
49
50 fn init() -> SectionConfig {
51 let obj_schema = match Remote::API_SCHEMA {
52 Schema::Object(ref obj_schema) => obj_schema,
53 _ => unreachable!(),
54 };
55
56 let plugin = SectionConfigPlugin::new("remote".to_string(), obj_schema);
57 let mut config = SectionConfig::new(&REMOTE_ID_SCHEMA);
58 config.register_plugin(plugin);
59
60 config
61 }
62
63 pub const REMOTES_CFG_FILENAME: &str = "/etc/proxmox-backup/remotes.cfg";
64 pub const REMOTES_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.remotes.lck";
65
66 pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
67 let content = match std::fs::read_to_string(REMOTES_CFG_FILENAME) {
68 Ok(c) => c,
69 Err(err) => {
70 if err.kind() == std::io::ErrorKind::NotFound {
71 String::from("")
72 } else {
73 bail!("unable to read '{}' - {}", REMOTES_CFG_FILENAME, err);
74 }
75 }
76 };
77
78 let digest = openssl::sha::sha256(content.as_bytes());
79 let data = CONFIG.parse(REMOTES_CFG_FILENAME, &content)?;
80 Ok((data, digest))
81 }
82
83 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
84 let raw = CONFIG.write(REMOTES_CFG_FILENAME, &config)?;
85
86 let backup_user = crate::backup::backup_user()?;
87 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
88 // set the correct owner/group/permissions while saving file
89 // owner(rw) = root, group(r)= backup
90 let options = CreateOptions::new()
91 .perm(mode)
92 .owner(nix::unistd::ROOT)
93 .group(backup_user.gid);
94
95 replace_file(REMOTES_CFG_FILENAME, raw.as_bytes(), options)?;
96
97 Ok(())
98 }
99
100 // shell completion helper
101 pub fn complete_remote_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
102 match config() {
103 Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
104 Err(_) => return vec![],
105 }
106 }