-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathserver.rs
138 lines (116 loc) · 4.47 KB
/
server.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Routines and data structures for working with Propolis server processes.
use std::{fmt::Debug, net::SocketAddrV4, os::unix::process::CommandExt};
use anyhow::Result;
use camino::{Utf8Path, Utf8PathBuf};
use tracing::{debug, info};
use crate::server_log_mode::ServerLogMode;
/// Parameters used to launch and configure the Propolis server process. These
/// are distinct from the parameters used to configure the VM that that process
/// will host.
#[derive(Clone, Debug)]
pub struct ServerProcessParameters<'a> {
/// The path to the server binary to launch.
pub server_path: Utf8PathBuf,
/// The directory in which to find or place files that are read or written
/// by this server process.
pub data_dir: &'a Utf8Path,
/// The address at which the server should serve.
pub server_addr: SocketAddrV4,
/// The address at which the server should offer its VNC server.
pub vnc_addr: SocketAddrV4,
pub log_mode: ServerLogMode,
}
pub struct PropolisServer {
server: std::process::Child,
address: SocketAddrV4,
}
impl PropolisServer {
pub(crate) fn new(
vm_name: &str,
process_params: ServerProcessParameters,
config_toml_path: &Utf8Path,
) -> Result<Self> {
let ServerProcessParameters {
server_path,
data_dir,
server_addr,
vnc_addr,
log_mode,
} = process_params;
info!(
?server_path,
?config_toml_path,
?server_addr,
"Launching Propolis server"
);
let (server_stdout, server_stderr) =
log_mode.get_handles(&data_dir, vm_name)?;
let mut server_cmd = std::process::Command::new("pfexec");
server_cmd
.args([
server_path.as_str(),
"run",
config_toml_path.as_str(),
server_addr.to_string().as_str(),
vnc_addr.to_string().as_str(),
])
.stdout(server_stdout)
.stderr(server_stderr);
// Gracefully shutting down a Propolis server requires PHD to send an
// instance stop request to the server before it is actually terminated.
// This ensures that the server has a chance to clean up kernel VMM
// resources. It's desirable for the server to do this and not PHD
// because a failure to clean up VMMs on stop is a Propolis bug.
//
// The PHD runner sets up a SIGINT handler that tries to give the
// framework an opportunity to issue these requests before the runner
// exits. However, pressing Ctrl-C in a shell will typically broadcast
// SIGINT to all of the processes in the foreground process's group, not
// just to the foreground process itself. This means that a Ctrl-C press
// will usually kill all of PHD's Propolis servers before the cleanup
// logic can run.
//
// To avoid this problem, add a pre-`exec` hook that directs Propolis
// servers to ignore SIGINT. On Ctrl-C, the runner will drop all active
// `TestVm`s, and this drop path (if allowed to complete) will kill all
// these processes.
unsafe {
server_cmd.pre_exec(move || {
libc::signal(libc::SIGINT, libc::SIG_IGN);
Ok(())
});
}
let server = PropolisServer {
server: server_cmd.spawn()?,
address: server_addr,
};
info!("Launched server with pid {}", server.server.id());
Ok(server)
}
pub(crate) fn server_addr(&self) -> SocketAddrV4 {
self.address
}
}
impl Drop for PropolisServer {
fn drop(&mut self) {
let pid = self.server.id().to_string();
debug!(
pid,
%self.address,
"Killing Propolis server that was dropped"
);
std::process::Command::new("pfexec")
.args(["kill", &pid])
.spawn()
.expect("should be able to kill a phd-spawned propolis");
self.server
.wait()
.expect("should be able to wait on a phd-spawned propolis");
debug!(pid,
%self.address,
"Successfully waited for demise of Propolis server that was dropped");
}
}