]> git.proxmox.com Git - proxmox-backup.git/blame - src/bin/proxmox-restore-daemon.rs
restore daemon: add setup_system_env helper
[proxmox-backup.git] / src / bin / proxmox-restore-daemon.rs
CommitLineData
dd9cef56
SR
1///! Daemon binary to run inside a micro-VM for secure single file restore of disk images
2use anyhow::{bail, format_err, Error};
d32a8652 3use lazy_static::lazy_static;
9a06eb16 4use log::{info, error};
dd9cef56
SR
5
6use std::os::unix::{
7 io::{FromRawFd, RawFd},
8 net,
9};
10use std::path::Path;
d32a8652 11use std::sync::{Arc, Mutex};
dd9cef56
SR
12
13use tokio::sync::mpsc;
14use tokio_stream::wrappers::ReceiverStream;
15
2b7f8dd5
WB
16use pbs_client::DEFAULT_VSOCK_PORT;
17
dd9cef56 18use proxmox::api::RpcEnvironmentType;
dd9cef56
SR
19use proxmox_backup::server::{rest::*, ApiConfig};
20
21mod proxmox_restore_daemon;
22use proxmox_restore_daemon::*;
23
24/// Maximum amount of pending requests. If saturated, virtio-vsock returns ETIMEDOUT immediately.
25/// We should never have more than a few requests in queue, so use a low number.
26pub const MAX_PENDING: usize = 32;
27
28/// Will be present in base initramfs
29pub const VM_DETECT_FILE: &str = "/restore-vm-marker";
30
d32a8652
SR
31lazy_static! {
32 /// The current disks state. Use for accessing data on the attached snapshots.
33 pub static ref DISK_STATE: Arc<Mutex<DiskState>> = {
34 Arc::new(Mutex::new(DiskState::scan().unwrap()))
35 };
36}
37
dd9cef56
SR
38/// This is expected to be run by 'proxmox-file-restore' within a mini-VM
39fn main() -> Result<(), Error> {
40 if !Path::new(VM_DETECT_FILE).exists() {
309e14eb
TL
41 bail!(
42 "This binary is not supposed to be run manually, use 'proxmox-file-restore' instead."
43 );
dd9cef56
SR
44 }
45
46 // don't have a real syslog (and no persistance), so use env_logger to print to a log file (via
47 // stdout to a serial terminal attached by QEMU)
48 env_logger::from_env(env_logger::Env::default().default_filter_or("info"))
49 .write_style(env_logger::WriteStyle::Never)
ecd66eca 50 .format_timestamp_millis()
dd9cef56
SR
51 .init();
52
73e1ba65 53 setup_system_env()?;
33d7292f 54
d32a8652
SR
55 // scan all attached disks now, before starting the API
56 // this will panic and stop the VM if anything goes wrong
9a06eb16 57 info!("scanning all disks...");
d32a8652
SR
58 {
59 let _disk_state = DISK_STATE.lock().unwrap();
60 }
61
9a06eb16
TL
62 info!("disk scan complete, starting main runtime...");
63
d420962f 64 pbs_runtime::main(run())
dd9cef56
SR
65}
66
73e1ba65
TL
67/// ensure we have our /run dirs, system users and stuff like that setup
68fn setup_system_env() -> Result<(), Error> {
69 // the API may save some stuff there, e.g., the memcon tracking file
70 // we do not care much, but it's way less headache to just create it
71 std::fs::create_dir_all("/run/proxmox-backup")?;
72
73 Ok(())
74}
75
dd9cef56 76async fn run() -> Result<(), Error> {
a26ebad5
SR
77 watchdog_init();
78
dd9cef56
SR
79 let auth_config = Arc::new(
80 auth::ticket_auth().map_err(|err| format_err!("reading ticket file failed: {}", err))?,
81 );
82 let config = ApiConfig::new("", &ROUTER, RpcEnvironmentType::PUBLIC, auth_config)?;
83 let rest_server = RestServer::new(config);
84
85 let vsock_fd = get_vsock_fd()?;
86 let connections = accept_vsock_connections(vsock_fd);
87 let receiver_stream = ReceiverStream::new(connections);
88 let acceptor = hyper::server::accept::from_stream(receiver_stream);
89
90 hyper::Server::builder(acceptor).serve(rest_server).await?;
91
92 bail!("hyper server exited");
93}
94
95fn accept_vsock_connections(
96 vsock_fd: RawFd,
97) -> mpsc::Receiver<Result<tokio::net::UnixStream, Error>> {
98 use nix::sys::socket::*;
99 let (sender, receiver) = mpsc::channel(MAX_PENDING);
100
101 tokio::spawn(async move {
102 loop {
103 let stream: Result<tokio::net::UnixStream, Error> = tokio::task::block_in_place(|| {
104 // we need to accept manually, as UnixListener aborts if socket type != AF_UNIX ...
105 let client_fd = accept(vsock_fd)?;
106 let stream = unsafe { net::UnixStream::from_raw_fd(client_fd) };
107 stream.set_nonblocking(true)?;
108 tokio::net::UnixStream::from_std(stream).map_err(|err| err.into())
109 });
110
111 match stream {
112 Ok(stream) => {
113 if sender.send(Ok(stream)).await.is_err() {
114 error!("connection accept channel was closed");
115 }
116 }
117 Err(err) => {
118 error!("error accepting vsock connetion: {}", err);
119 }
120 }
121 }
122 });
123
124 receiver
125}
126
127fn get_vsock_fd() -> Result<RawFd, Error> {
128 use nix::sys::socket::*;
129 let sock_fd = socket(
130 AddressFamily::Vsock,
131 SockType::Stream,
132 SockFlag::empty(),
133 None,
134 )?;
135 let sock_addr = VsockAddr::new(libc::VMADDR_CID_ANY, DEFAULT_VSOCK_PORT as u32);
136 bind(sock_fd, &SockAddr::Vsock(sock_addr))?;
137 listen(sock_fd, MAX_PENDING)?;
138 Ok(sock_fd)
139}