]> git.proxmox.com Git - proxmox-backup.git/blob - proxmox-rest-server/src/lib.rs
9c2dbb08e87eff068737c4c6b32e23abada6881b
[proxmox-backup.git] / proxmox-rest-server / src / lib.rs
1 use std::os::unix::io::RawFd;
2 use std::sync::atomic::{Ordering, AtomicBool};
3
4 use anyhow::{bail, format_err, Error};
5 use nix::unistd::Pid;
6
7 use proxmox::tools::fd::Fd;
8 use proxmox::sys::linux::procfs::PidStat;
9 use proxmox::api::UserInformation;
10 use proxmox::tools::fs::CreateOptions;
11
12 mod compression;
13 pub use compression::*;
14
15 pub mod daemon;
16
17 pub mod formatter;
18
19 mod environment;
20 pub use environment::*;
21
22 mod state;
23 pub use state::*;
24
25 mod command_socket;
26 pub use command_socket::*;
27
28 mod file_logger;
29 pub use file_logger::{FileLogger, FileLogOptions};
30
31 mod api_config;
32 pub use api_config::ApiConfig;
33
34 mod rest;
35 pub use rest::RestServer;
36
37 mod worker_task;
38 pub use worker_task::*;
39
40 mod h2service;
41 pub use h2service::*;
42
43 pub enum AuthError {
44 Generic(Error),
45 NoData,
46 }
47
48 impl From<Error> for AuthError {
49 fn from(err: Error) -> Self {
50 AuthError::Generic(err)
51 }
52 }
53
54 pub trait ApiAuth {
55 fn check_auth(
56 &self,
57 headers: &http::HeaderMap,
58 method: &hyper::Method,
59 ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError>;
60 }
61
62 lazy_static::lazy_static!{
63 static ref PID: i32 = unsafe { libc::getpid() };
64 static ref PSTART: u64 = PidStat::read_from_pid(Pid::from_raw(*PID)).unwrap().starttime;
65 }
66
67 pub fn pid() -> i32 {
68 *PID
69 }
70
71 pub fn pstart() -> u64 {
72 *PSTART
73 }
74
75 pub fn write_pid(pid_fn: &str) -> Result<(), Error> {
76 let pid_str = format!("{}\n", *PID);
77 proxmox::tools::fs::replace_file(pid_fn, pid_str.as_bytes(), CreateOptions::new())
78 }
79
80 pub fn read_pid(pid_fn: &str) -> Result<i32, Error> {
81 let pid = proxmox::tools::fs::file_get_contents(pid_fn)?;
82 let pid = std::str::from_utf8(&pid)?.trim();
83 pid.parse().map_err(|err| format_err!("could not parse pid - {}", err))
84 }
85
86 pub fn ctrl_sock_from_pid(pid: i32) -> String {
87 // Note: The control socket always uses @/run/proxmox-backup/ as prefix
88 // for historc reason.
89 format!("\0{}/control-{}.sock", "/run/proxmox-backup", pid)
90 }
91
92 pub fn our_ctrl_sock() -> String {
93 ctrl_sock_from_pid(*PID)
94 }
95
96 static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false);
97
98 pub fn request_shutdown() {
99 println!("request_shutdown");
100 SHUTDOWN_REQUESTED.store(true, Ordering::SeqCst);
101 crate::server_shutdown();
102 }
103
104 #[inline(always)]
105 pub fn shutdown_requested() -> bool {
106 SHUTDOWN_REQUESTED.load(Ordering::SeqCst)
107 }
108
109 pub fn fail_on_shutdown() -> Result<(), Error> {
110 if shutdown_requested() {
111 bail!("Server shutdown requested - aborting task");
112 }
113 Ok(())
114 }
115
116 /// Helper to set/clear the FD_CLOEXEC flag on file descriptors
117 pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), Error> {
118 use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
119 let mut flags = FdFlag::from_bits(fcntl(fd, F_GETFD)?)
120 .ok_or_else(|| format_err!("unhandled file flags"))?; // nix crate is stupid this way...
121 flags.set(FdFlag::FD_CLOEXEC, on);
122 fcntl(fd, F_SETFD(flags))?;
123 Ok(())
124 }
125
126 /// safe wrapper for `nix::sys::socket::socketpair` defaulting to `O_CLOEXEC` and guarding the file
127 /// descriptors.
128 pub fn socketpair() -> Result<(Fd, Fd), Error> {
129 use nix::sys::socket;
130 let (pa, pb) = socket::socketpair(
131 socket::AddressFamily::Unix,
132 socket::SockType::Stream,
133 None,
134 socket::SockFlag::SOCK_CLOEXEC,
135 )?;
136 Ok((Fd(pa), Fd(pb)))
137 }
138
139
140 /// Extract a specific cookie from cookie header.
141 /// We assume cookie_name is already url encoded.
142 pub fn extract_cookie(cookie: &str, cookie_name: &str) -> Option<String> {
143 for pair in cookie.split(';') {
144 let (name, value) = match pair.find('=') {
145 Some(i) => (pair[..i].trim(), pair[(i + 1)..].trim()),
146 None => return None, // Cookie format error
147 };
148
149 if name == cookie_name {
150 use percent_encoding::percent_decode;
151 if let Ok(value) = percent_decode(value.as_bytes()).decode_utf8() {
152 return Some(value.into());
153 } else {
154 return None; // Cookie format error
155 }
156 }
157 }
158
159 None
160 }
161
162 /// normalize uri path
163 ///
164 /// Do not allow ".", "..", or hidden files ".XXXX"
165 /// Also remove empty path components
166 pub fn normalize_uri_path(path: &str) -> Result<(String, Vec<&str>), Error> {
167 let items = path.split('/');
168
169 let mut path = String::new();
170 let mut components = vec![];
171
172 for name in items {
173 if name.is_empty() {
174 continue;
175 }
176 if name.starts_with('.') {
177 bail!("Path contains illegal components.");
178 }
179 path.push('/');
180 path.push_str(name);
181 components.push(name);
182 }
183
184 Ok((path, components))
185 }