]> git.proxmox.com Git - proxmox-backup.git/blob - proxmox-rest-server/src/lib.rs
proxmox-rest-server: cleanup formatter, improve docs
[proxmox-backup.git] / proxmox-rest-server / src / lib.rs
1 use std::os::unix::io::RawFd;
2
3 use anyhow::{bail, format_err, Error};
4 use nix::unistd::Pid;
5
6 use proxmox::tools::fd::Fd;
7 use proxmox::sys::linux::procfs::PidStat;
8 use proxmox::api::UserInformation;
9 use proxmox::tools::fs::CreateOptions;
10
11 mod compression;
12 pub use compression::*;
13
14 pub mod daemon;
15
16 pub mod formatter;
17
18 mod environment;
19 pub use environment::*;
20
21 mod state;
22 pub use state::*;
23
24 mod command_socket;
25 pub use command_socket::*;
26
27 mod file_logger;
28 pub use file_logger::{FileLogger, FileLogOptions};
29
30 mod api_config;
31 pub use api_config::ApiConfig;
32
33 mod rest;
34 pub use rest::RestServer;
35
36 mod worker_task;
37 pub use worker_task::*;
38
39 mod h2service;
40 pub use h2service::*;
41
42 pub enum AuthError {
43 Generic(Error),
44 NoData,
45 }
46
47 impl From<Error> for AuthError {
48 fn from(err: Error) -> Self {
49 AuthError::Generic(err)
50 }
51 }
52
53 pub trait ApiAuth {
54 fn check_auth(
55 &self,
56 headers: &http::HeaderMap,
57 method: &hyper::Method,
58 ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError>;
59 }
60
61 lazy_static::lazy_static!{
62 static ref PID: i32 = unsafe { libc::getpid() };
63 static ref PSTART: u64 = PidStat::read_from_pid(Pid::from_raw(*PID)).unwrap().starttime;
64 }
65
66 pub fn pid() -> i32 {
67 *PID
68 }
69
70 pub fn pstart() -> u64 {
71 *PSTART
72 }
73
74 pub fn write_pid(pid_fn: &str) -> Result<(), Error> {
75 let pid_str = format!("{}\n", *PID);
76 proxmox::tools::fs::replace_file(pid_fn, pid_str.as_bytes(), CreateOptions::new())
77 }
78
79 pub fn read_pid(pid_fn: &str) -> Result<i32, Error> {
80 let pid = proxmox::tools::fs::file_get_contents(pid_fn)?;
81 let pid = std::str::from_utf8(&pid)?.trim();
82 pid.parse().map_err(|err| format_err!("could not parse pid - {}", err))
83 }
84
85 pub fn ctrl_sock_from_pid(pid: i32) -> String {
86 // Note: The control socket always uses @/run/proxmox-backup/ as prefix
87 // for historc reason.
88 format!("\0{}/control-{}.sock", "/run/proxmox-backup", pid)
89 }
90
91 pub fn our_ctrl_sock() -> String {
92 ctrl_sock_from_pid(*PID)
93 }
94
95 static mut SHUTDOWN_REQUESTED: bool = false;
96
97 pub fn request_shutdown() {
98 unsafe {
99 SHUTDOWN_REQUESTED = true;
100 }
101 crate::server_shutdown();
102 }
103
104 #[inline(always)]
105 pub fn shutdown_requested() -> bool {
106 unsafe { SHUTDOWN_REQUESTED }
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 }