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