]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/mod.rs
move channel/stream helpers to pbs-tools
[proxmox-backup.git] / src / tools / mod.rs
CommitLineData
51b499db
DM
1//! Tools and utilities
2//!
3//! This is a collection of small and useful tools.
6100071f 4use std::any::Any;
98c259b4 5use std::os::unix::io::RawFd;
365bb90f 6
f7d4e4b5 7use anyhow::{bail, format_err, Error};
c5946faf 8use openssl::hash::{hash, DigestBytes, MessageDigest};
0fe5d605 9
00ec8d16 10pub use proxmox::tools::fd::Fd;
95f36925 11use proxmox::tools::fs::{create_path, CreateOptions};
00ec8d16 12
1d781c5b 13use proxmox_http::{
7d2be91b
FG
14 client::SimpleHttp,
15 client::SimpleHttpOptions,
16 ProxyConfig,
17};
57889533 18
a5951b4f 19pub use pbs_tools::json;
18cdf20a 20pub use pbs_tools::nom;
e57841c4 21pub use pbs_tools::{run_command, command_output, command_output_as_string};
83771aa0
WB
22pub use pbs_tools::process_locker::{
23 ProcessLocker, ProcessLockExclusiveGuard, ProcessLockSharedGuard
24};
e57841c4 25
e6513bd5 26pub mod apt;
556eb70e 27pub mod async_io;
ea62611d 28pub mod compression;
bc5c1a9a 29pub mod config;
a5bdc987 30pub mod cpio;
6100071f 31pub mod daemon;
10effc98 32pub mod disks;
fb01fd3a 33pub mod fuse_loop;
e5ef69ec 34
fda19dcc
DM
35mod memcom;
36pub use memcom::Memcom;
37
8074d2b0 38pub mod logrotate;
45f9b32e 39pub mod loopdev;
59e94227 40pub mod serde_filter;
fb01fd3a 41pub mod statistics;
7b22fb25 42pub mod subscription;
fb01fd3a
WB
43pub mod systemd;
44pub mod ticket;
9372c078 45pub mod sgutils2;
639a6782 46pub mod paperkey;
f1d99e3f 47
fb01fd3a
WB
48pub mod parallel_handler;
49pub use parallel_handler::ParallelHandler;
3c9b3702 50
3b151414 51mod file_logger;
fb01fd3a 52pub use file_logger::{FileLogger, FileLogOptions};
3b151414 53
4805edc4 54pub use pbs_tools::broadcast_future::{BroadcastData, BroadcastFuture};
2b7f8dd5 55pub use pbs_tools::ops::ControlFlow;
490be29e 56
fded74d0 57/// The `BufferedRead` trait provides a single function
0a72e267
DM
58/// `buffered_read`. It returns a reference to an internal buffer. The
59/// purpose of this traid is to avoid unnecessary data copies.
fded74d0 60pub trait BufferedRead {
318564ac
DM
61 /// This functions tries to fill the internal buffers, then
62 /// returns a reference to the available data. It returns an empty
63 /// buffer if `offset` points to the end of the file.
0a72e267
DM
64 fn buffered_read(&mut self, offset: u64) -> Result<&[u8], Error>;
65}
66
c5946faf
WB
67/// Shortcut for md5 sums.
68pub fn md5sum(data: &[u8]) -> Result<DigestBytes, Error> {
69 hash(MessageDigest::md5(), data).map_err(Error::from)
70}
71
7e13b2d6 72pub fn get_hardware_address() -> Result<String, Error> {
1631c54f 73 static FILENAME: &str = "/etc/ssh/ssh_host_rsa_key.pub";
7e13b2d6 74
72c0e102
TL
75 let contents = proxmox::tools::fs::file_get_contents(FILENAME)
76 .map_err(|e| format_err!("Error getting host key - {}", e))?;
77 let digest = md5sum(&contents)
78 .map_err(|e| format_err!("Error digesting host key - {}", e))?;
7e13b2d6 79
52fe9e8e 80 Ok(proxmox::tools::bin_to_hex(&digest).to_uppercase())
7e13b2d6 81}
22968600 82
af2fddea
DM
83pub fn assert_if_modified(digest1: &str, digest2: &str) -> Result<(), Error> {
84 if digest1 != digest2 {
6100071f 85 bail!("detected modified configuration - file changed by other user? Try again.");
af2fddea
DM
86 }
87 Ok(())
88}
b9903d63 89
09f12d1c 90/// Extract a specific cookie from cookie header.
b9903d63 91/// We assume cookie_name is already url encoded.
09f12d1c 92pub fn extract_cookie(cookie: &str, cookie_name: &str) -> Option<String> {
b9903d63 93 for pair in cookie.split(';') {
b9903d63
DM
94 let (name, value) = match pair.find('=') {
95 Some(i) => (pair[..i].trim(), pair[(i + 1)..].trim()),
96 None => return None, // Cookie format error
97 };
98
99 if name == cookie_name {
8a1028e0 100 use percent_encoding::percent_decode;
b9903d63
DM
101 if let Ok(value) = percent_decode(value.as_bytes()).decode_utf8() {
102 return Some(value.into());
103 } else {
104 return None; // Cookie format error
105 }
106 }
107 }
108
109 None
110}
af53186e 111
002a191a
DM
112/// Detect modified configuration files
113///
add5861e 114/// This function fails with a reasonable error message if checksums do not match.
002a191a
DM
115pub fn detect_modified_configuration_file(digest1: &[u8;32], digest2: &[u8;32]) -> Result<(), Error> {
116 if digest1 != digest2 {
a4ba60be 117 bail!("detected modified configuration - file changed by other user? Try again.");
002a191a
DM
118 }
119 Ok(())
120}
121
3578d99f
DM
122/// normalize uri path
123///
124/// Do not allow ".", "..", or hidden files ".XXXX"
125/// Also remove empty path components
126pub fn normalize_uri_path(path: &str) -> Result<(String, Vec<&str>), Error> {
3578d99f
DM
127 let items = path.split('/');
128
129 let mut path = String::new();
130 let mut components = vec![];
131
132 for name in items {
6100071f
WB
133 if name.is_empty() {
134 continue;
135 }
62ee2eb4 136 if name.starts_with('.') {
3578d99f
DM
137 bail!("Path contains illegal components.");
138 }
139 path.push('/');
140 path.push_str(name);
141 components.push(name);
142 }
143
144 Ok((path, components))
145}
146
ff7049d4 147pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), Error> {
6100071f 148 use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
ff7049d4
WB
149 let mut flags = FdFlag::from_bits(fcntl(fd, F_GETFD)?)
150 .ok_or_else(|| format_err!("unhandled file flags"))?; // nix crate is stupid this way...
151 flags.set(FdFlag::FD_CLOEXEC, on);
152 fcntl(fd, F_SETFD(flags))?;
153 Ok(())
154}
9136f857 155
9136f857
DM
156static mut SHUTDOWN_REQUESTED: bool = false;
157
158pub fn request_shutdown() {
6100071f
WB
159 unsafe {
160 SHUTDOWN_REQUESTED = true;
161 }
7a630df7 162 crate::server::server_shutdown();
9136f857
DM
163}
164
165#[inline(always)]
166pub fn shutdown_requested() -> bool {
167 unsafe { SHUTDOWN_REQUESTED }
168}
92da93b2
DM
169
170pub fn fail_on_shutdown() -> Result<(), Error> {
171 if shutdown_requested() {
172 bail!("Server shutdown requested - aborting task");
173 }
174 Ok(())
175}
d96bb7f1 176
c4044009
WB
177/// safe wrapper for `nix::unistd::pipe2` defaulting to `O_CLOEXEC` and guarding the file
178/// descriptors.
efd1536e
WB
179pub fn pipe() -> Result<(Fd, Fd), Error> {
180 let (pin, pout) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
181 Ok((Fd(pin), Fd(pout)))
182}
2edc341b 183
c4044009
WB
184/// safe wrapper for `nix::sys::socket::socketpair` defaulting to `O_CLOEXEC` and guarding the file
185/// descriptors.
186pub fn socketpair() -> Result<(Fd, Fd), Error> {
187 use nix::sys::socket;
188 let (pa, pb) = socket::socketpair(
189 socket::AddressFamily::Unix,
190 socket::SockType::Stream,
191 None,
192 socket::SockFlag::SOCK_CLOEXEC,
193 )?;
194 Ok((Fd(pa), Fd(pb)))
195}
196
197
2edc341b
DM
198/// An easy way to convert types to Any
199///
200/// Mostly useful to downcast trait objects (see RpcEnvironment).
201pub trait AsAny {
dd5495d6 202 fn as_any(&self) -> &dyn Any;
2edc341b
DM
203}
204
205impl<T: Any> AsAny for T {
6100071f
WB
206 fn as_any(&self) -> &dyn Any {
207 self
208 }
2edc341b 209}
8a1028e0 210
32413921
FG
211/// The default 2 hours are far too long for PBS
212pub const PROXMOX_BACKUP_TCP_KEEPALIVE_TIME: u32 = 120;
57889533
FG
213pub const DEFAULT_USER_AGENT_STRING: &'static str = "proxmox-backup-client/1.0";
214
215/// Returns a new instance of `SimpleHttp` configured for PBS usage.
216pub fn pbs_simple_http(proxy_config: Option<ProxyConfig>) -> SimpleHttp {
217 let options = SimpleHttpOptions {
218 proxy_config,
219 user_agent: Some(DEFAULT_USER_AGENT_STRING.to_string()),
220 tcp_keepalive: Some(PROXMOX_BACKUP_TCP_KEEPALIVE_TIME),
221 ..Default::default()
222 };
223
224 SimpleHttp::with_options(options)
225}
32413921 226
ac7513e3
DM
227pub fn setup_safe_path_env() {
228 std::env::set_var("PATH", "/sbin:/bin:/usr/sbin:/usr/bin");
229 // Make %ENV safer - as suggested by https://perldoc.perl.org/perlsec.html
230 for name in &["IFS", "CDPATH", "ENV", "BASH_ENV"] {
231 std::env::remove_var(name);
232 }
233}
cdf1da28 234
014dc5f9
WB
235/// Create the base run-directory.
236///
237/// This exists to fixate the permissions for the run *base* directory while allowing intermediate
238/// directories after it to have different permissions.
239pub fn create_run_dir() -> Result<(), Error> {
95f36925
WB
240 let backup_user = crate::backup::backup_user()?;
241 let opts = CreateOptions::new()
242 .owner(backup_user.uid)
243 .group(backup_user.gid);
af06decd 244 let _: bool = create_path(pbs_buildcfg::PROXMOX_BACKUP_RUN_DIR_M!(), None, Some(opts))?;
014dc5f9
WB
245 Ok(())
246}