]>
Commit | Line | Data |
---|---|---|
51d84f98 | 1 | use std::os::unix::io::RawFd; |
5b724780 | 2 | use std::sync::atomic::{Ordering, AtomicBool}; |
51d84f98 DM |
3 | |
4 | use anyhow::{bail, format_err, Error}; | |
e8c124fe | 5 | use nix::unistd::Pid; |
51d84f98 DM |
6 | |
7 | use proxmox::tools::fd::Fd; | |
e8c124fe | 8 | use proxmox::sys::linux::procfs::PidStat; |
2ea6f8d0 | 9 | use proxmox::api::UserInformation; |
e8c124fe | 10 | use proxmox::tools::fs::CreateOptions; |
51d84f98 | 11 | |
fbe0de85 DM |
12 | mod compression; |
13 | pub use compression::*; | |
14 | ||
51d84f98 | 15 | pub mod daemon; |
962553d2 | 16 | |
dc28aa1a | 17 | pub mod formatter; |
ca7a2616 | 18 | |
ba04dfb9 DM |
19 | mod environment; |
20 | pub use environment::*; | |
21 | ||
ca7a2616 DM |
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 | ||
1d60abf9 | 34 | mod rest; |
85ec987a | 35 | pub use rest::RestServer; |
1d60abf9 | 36 | |
e8c124fe DM |
37 | mod worker_task; |
38 | pub use worker_task::*; | |
39 | ||
85ec987a DM |
40 | mod h2service; |
41 | pub use h2service::*; | |
42 | ||
9cb2c97c | 43 | /// Authentification Error |
ca7a2616 DM |
44 | pub enum AuthError { |
45 | Generic(Error), | |
46 | NoData, | |
47 | } | |
48 | ||
49 | impl From<Error> for AuthError { | |
50 | fn from(err: Error) -> Self { | |
51 | AuthError::Generic(err) | |
52 | } | |
53 | } | |
54 | ||
9cb2c97c | 55 | /// User Authentification trait |
ca7a2616 | 56 | pub 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 |
68 | lazy_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 |
76 | pub 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 |
83 | pub fn pstart() -> u64 { |
84 | *PSTART | |
85 | } | |
86 | ||
9cb2c97c | 87 | /// Helper to write the PID into a file |
e8c124fe DM |
88 | pub 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 |
94 | pub 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 |
105 | pub 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 |
112 | pub fn our_ctrl_sock() -> String { |
113 | ctrl_sock_from_pid(*PID) | |
114 | } | |
115 | ||
5b724780 | 116 | static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false); |
ca7a2616 | 117 | |
9cb2c97c | 118 | /// Request a server shutdown (usually called from [catch_shutdown_signal]) |
ca7a2616 | 119 | pub 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)] |
126 | pub 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 |
131 | pub 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 |
139 | pub 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. | |
150 | pub 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. | |
164 | pub 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 | |
188 | pub 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 | } |