Skip to content

Commit

Permalink
acond: Add signal/kill interface
Browse files Browse the repository at this point in the history
Signed-off-by: Liu, Xiangquan <[email protected]>

Fix review comments.
  • Loading branch information
xiangquanliu committed Sep 11, 2023
1 parent ffa0b95 commit 3806f81
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 59 deletions.
18 changes: 14 additions & 4 deletions acond/proto/acon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ message RestartRequest {
uint64 timeout = 2;
}

message RestartResponse {
uint32 container_id = 1;
}

message ExecRequest {
uint32 container_id = 1;
string command = 2;
Expand All @@ -58,6 +54,11 @@ message ExecResponse {
bytes stderr = 2;
}

message KillRequest {
uint32 container_id = 1;
int32 signal_num = 2;
}

message InspectRequest {
uint32 container_id = 1;
}
Expand Down Expand Up @@ -170,6 +171,15 @@ service AconService {
// On failure, returns the specified error.
rpc Exec(ExecRequest) returns (ExecResponse);

// Sends signal which must exist in the corresponding manifest to an existing container.
//
// container_id specifies the Container to which the specified sinal will be sent. The signal must
// exist in the corresponding manifest. Otherwise, it fails with errors.
//
// On success, returns OK.
// On failure, returns the specified error.
rpc Kill(KillRequest) returns (google.protobuf.Empty);

// Retrieves information of the specified Containers.
//
// container_id specifies the Container whose status to be retrieved. If container_id is 0 (zero),
Expand Down
116 changes: 85 additions & 31 deletions acond/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
use grpc::acon_service_server::{AconService, AconServiceServer};
use grpc::{
AddBlobRequest, AddManifestRequest, AddManifestResponse, ContainerInfo, ExecRequest,
ExecResponse, GetManifestRequest, GetManifestResponse, InspectRequest, InspectResponse, MrLog,
ReportRequest, ReportResponse, RestartRequest, StartRequest, StartResponse,
ExecResponse, GetManifestRequest, GetManifestResponse, InspectRequest, InspectResponse,
KillRequest, MrLog, ReportRequest, ReportResponse, RestartRequest, StartRequest, StartResponse,
};
use nix::errno::Errno;
use std::{collections::HashMap, sync::Arc};
use tokio::{
sync::RwLock,
Expand Down Expand Up @@ -46,14 +47,14 @@ impl AconService for TDAconService {
let mut pod = ref_pod.write().await;

if pod.finalized {
return Err(Status::unknown(utils::ERR_RPC_REJECT_MANIFEST));
return Err(Status::permission_denied(utils::ERR_RPC_REJECT_MANIFEST));
}

let verified = utils::verify_signature(manifest_bytes, signature_bytes, signer_bytes)
.map_err(|e| Status::unknown(e.to_string()))?;

if !verified {
return Err(Status::unknown(utils::ERR_RPC_INVALID_SIGNATURE));
return Err(Status::invalid_argument(utils::ERR_RPC_INVALID_SIGNATURE));
}

// verify contents of manifest.
Expand Down Expand Up @@ -93,7 +94,7 @@ impl AconService for TDAconService {
.is_manifest_accepted(&image)
.map_err(|e| Status::unknown(e.to_string()))?;
if !is_accepted {
return Err(Status::unknown(utils::ERR_RPC_REJECT_MANIFEST));
return Err(Status::permission_denied(utils::ERR_RPC_REJECT_MANIFEST));
}

utils::create_alias_link(&image).map_err(|e| Status::unknown(e.to_string()))?;
Expand All @@ -120,7 +121,7 @@ impl AconService for TDAconService {
let mut pod = ref_pod.write().await;

if pod.finalized {
return Err(Status::unknown(utils::ERR_RPC_REJECT_MANIFEST));
return Err(Status::permission_denied(utils::ERR_RPC_REJECT_MANIFEST));
}

utils::measure_image(None).map_err(|e| Status::unknown(e.to_string()))?;
Expand All @@ -144,7 +145,7 @@ impl AconService for TDAconService {
let ref_pod = self.pod.clone();
let pod = ref_pod.read().await;
if !pod.is_blob_accepted(&layers) {
return Err(Status::unknown(utils::ERR_RPC_REJECT_BLOB));
return Err(Status::permission_denied(utils::ERR_RPC_REJECT_BLOB));
}

utils::save_blob(&layers, data).map_err(|e| Status::unknown(e.to_string()))?;
Expand All @@ -164,7 +165,7 @@ impl AconService for TDAconService {
let mut pod = ref_pod.write().await;
let image = pod
.get_image(&request.get_ref().image_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_IMAGE_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_IMAGE_ID))?;

let container = Container::start(image, &request.get_ref().envs)
.await
Expand Down Expand Up @@ -192,19 +193,21 @@ impl AconService for TDAconService {
let pod = ref_pod.read().await;
let container = pod
.get_container(&container_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
let image = pod
.get_image(&container.image_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_IMAGE_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_IMAGE_ID))?;

if image.manifest.no_restart {
return Err(Status::unknown(utils::ERR_RPC_CONTAINER_NOT_ALLOW_RESTART));
return Err(Status::permission_denied(
utils::ERR_RPC_CONTAINER_NOT_ALLOW_RESTART,
));
}

if container.is_running() {
if timeout == 0 {
return Err(Status::unknown(
utils::ERR_RPC_CONTAINER_KILL_TIMEOUT.replace("{}", "0"),
return Err(Status::deadline_exceeded(
utils::ERR_RPC_CONTAINER_RESTART_TIMEOUT,
));
}

Expand All @@ -213,19 +216,28 @@ impl AconService for TDAconService {
if s.abs() == libc::SIGTERM || s.abs() == libc::SIGKILL {
s
} else {
return Err(Status::unknown(utils::ERR_RPC_CONTAINER_NOT_ALLOW_KILL));
return Err(Status::permission_denied(
utils::ERR_RPC_CONTAINER_NOT_ALLOW_RESTART,
));
}
} else {
return Err(Status::unknown(utils::ERR_RPC_CONTAINER_NOT_ALLOW_KILL));
return Err(Status::permission_denied(
utils::ERR_RPC_CONTAINER_NOT_ALLOW_RESTART,
));
};

unsafe {
let pid = container.pid.into();
if sig > 0 {
libc::kill(pid, sig.abs());
} else {
libc::kill(-pid, sig.abs());
let mut pid: i32 = container.pid.into();
if sig < 0 {
pid = -pid.abs();
}

Errno::result(libc::kill(pid, sig.abs())).map_err(|errno| {
Status::unknown(
utils::ERR_RPC_SYSTEM_ERROR
.replace("{}", format!("{}", errno).as_str()),
)
})?;
}

Some(container.exit_notifier.as_ref().unwrap().clone())
Expand All @@ -238,8 +250,8 @@ impl AconService for TDAconService {
loop {
tokio::select! {
_ = time::sleep(Duration::from_secs(timeout)) => {
return Err(Status::unknown(
utils::ERR_RPC_CONTAINER_KILL_TIMEOUT.replace("{}", format!("{}", timeout).as_str()),
return Err(Status::deadline_exceeded(
utils::ERR_RPC_CONTAINER_RESTART_TIMEOUT,
));
}
_ = notifier.notified() => break,
Expand All @@ -252,17 +264,17 @@ impl AconService for TDAconService {
let pod = ref_pod.read().await;
let container = pod
.get_container(&container_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
pod.get_image(&container.image_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_IMAGE_ID))?
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_IMAGE_ID))?
.clone()
};

let ref_pod = self.pod.clone();
let mut pod = ref_pod.write().await;
let container = pod
.get_container_mut(&container_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
container
.restart(&image)
.await
Expand All @@ -289,9 +301,7 @@ impl AconService for TDAconService {
}

if stdin.len() > capture_size {
return Err(Status::invalid_argument(
utils::ERR_RPC_BUFFER_EXCEED.replace("{}", format!("{}", capture_size).as_str()),
));
return Err(Status::invalid_argument(utils::ERR_RPC_BUFFER_EXCEED));
}

if !utils::start_with_uppercase(command) {
Expand All @@ -304,7 +314,7 @@ impl AconService for TDAconService {
let pod = ref_pod.read().await;
let container = pod
.get_container(&container_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;

if !container.is_running() {
return Err(Status::unknown(utils::ERR_RPC_CONTAINER_EXITED));
Expand All @@ -322,6 +332,50 @@ impl AconService for TDAconService {
Ok(Response::new(ExecResponse { stdout, stderr }))
}

async fn kill(&self, request: Request<KillRequest>) -> Result<Response<()>, Status> {
let container_id = request.get_ref().container_id;
let signal_num = request.get_ref().signal_num;

let ref_pod = self.pod.clone();
let pod = ref_pod.read().await;
let container = pod
.get_container(&container_id)
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;

if !container.is_running() {
return Err(Status::unknown(utils::ERR_RPC_CONTAINER_EXITED));
}

let image = pod
.get_image(&container.image_id)
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_IMAGE_ID))?;

if let None = image.manifest.signals.iter().find(|&&s| s == signal_num) {
return Err(Status::permission_denied(
utils::ERR_RPC_CONTAINER_NOT_ALLOW_KILL,
));
}

unsafe {
let mut pid: i32 = container.pid.into();
if signal_num < 0 {
pid = -pid.abs();
}

Errno::result(libc::kill(pid, signal_num.abs())).map_err(|errno| {
Status::unknown(
utils::ERR_RPC_SYSTEM_ERROR.replace("{}", format!("{}", errno).as_str()),
)
})?;
}

if let Some(tx) = &pod.timeout_tx {
let _ = tx.send(false).await;
}

Ok(Response::new(()))
}

async fn inspect(
&self,
request: Request<InspectRequest>,
Expand Down Expand Up @@ -355,7 +409,7 @@ impl AconService for TDAconService {
} else {
let container = pod
.get_container_mut(&container_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_CONTAINER_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_CONTAINER_ID))?;

container
.update_status()
Expand Down Expand Up @@ -439,7 +493,7 @@ impl AconService for TDAconService {
let pod = ref_pod.read().await;
let image = pod
.get_image(image_id)
.ok_or_else(|| Status::unknown(utils::ERR_RPC_INVALID_IMAGE_ID))?;
.ok_or_else(|| Status::invalid_argument(utils::ERR_RPC_INVALID_IMAGE_ID))?;

let manifest = utils::get_manifest(image_id).map_err(|e| Status::unknown(e.to_string()))?;
let certificate = image.signer_bytes.clone();
Expand Down
48 changes: 24 additions & 24 deletions acond/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,31 @@ use std::{
use tar::Archive;

pub const REPORT_API_VERSION: &str = "1.0.0";
pub const ERR_CFG_INVALID_VSOCK_PORT: &str = "Invalid kernel parameter: vsock port.";
pub const ERR_CFG_INVALID_TCPIP_PORT: &str = "Invalid kernel parameter: TCP/IP port.";
pub const ERR_CFG_INVALID_TIMEOUT: &str = "Invalid kernel parameter: timeout.";
pub const ERR_RPC_REJECT_MANIFEST: &str = "Reject manifest according to the launch policy.";
pub const ERR_RPC_INVALID_SIGNATURE: &str = "Invalid enclave signature.";
pub const ERR_RPC_INVALID_CERTIFICATE: &str = "Invalid certificate.";
pub const ERR_RPC_INVALID_HASH_ALGORITHM: &str = "Invalid hash algorithm.";
pub const ERR_RPC_REJECT_BLOB: &str = "Reject blob as no existing manifests require the blob.";
pub const ERR_RPC_INVALID_IMAGE_ID: &str = "Invalid image id.";
pub const ERR_RPC_INVALID_CONTAINER_ID: &str = "Invalid container id.";
pub const ERR_RPC_CONTAINER_EXITED: &str = "Container has exited.";
pub const ERR_RPC_CONTAINER_NOT_ALLOW_KILL: &str = "Container is not allowed to be killed.";
pub const ERR_RPC_CONTAINER_KILL_TIMEOUT: &str = "Container can't be killed in {} seconds.";
pub const ERR_RPC_CONTAINER_NOT_ALLOW_RESTART: &str = "Container is not allowed to be restarted.";
pub const ERR_RPC_NO_IMAGES: &str = "No images in current TD.";
pub const ERR_RPC_INVALID_LPOLICY_FORMAT: &str = "Invalid launch policy format.";
pub const ERR_RPC_INVALID_MALIAS_FORMAT: &str = "Invalid manifest alias format.";
pub const ERR_CFG_INVALID_VSOCK_PORT: &str = "Invalid kernel parameter: vsock port";
pub const ERR_CFG_INVALID_TCPIP_PORT: &str = "Invalid kernel parameter: TCP/IP port";
pub const ERR_CFG_INVALID_TIMEOUT: &str = "Invalid kernel parameter: timeout";
pub const ERR_RPC_REJECT_MANIFEST: &str = "Manifest rejected according to the launch policy";
pub const ERR_RPC_INVALID_SIGNATURE: &str = "Invalid digital signature";
pub const ERR_RPC_INVALID_CERTIFICATE: &str = "Invalid certificate";
pub const ERR_RPC_INVALID_HASH_ALGORITHM: &str = "Invalid hash algorithm";
pub const ERR_RPC_REJECT_BLOB: &str = "Blob rejected as no manifests require the blob";
pub const ERR_RPC_INVALID_IMAGE_ID: &str = "Invalid Image ID";
pub const ERR_RPC_INVALID_CONTAINER_ID: &str = "Invalid Container ID";
pub const ERR_RPC_CONTAINER_EXITED: &str = "Container terminated";
pub const ERR_RPC_CONTAINER_RESTART_TIMEOUT: &str = "Timeout restarting container";
pub const ERR_RPC_CONTAINER_NOT_ALLOW_RESTART: &str = "Restarting container not allowed";
pub const ERR_RPC_CONTAINER_NOT_ALLOW_KILL: &str = "Signal not allowed";
pub const ERR_RPC_NO_IMAGES: &str = "No images in current TD";
pub const ERR_RPC_INVALID_LPOLICY_FORMAT: &str = "Invalid launch policy format";
pub const ERR_RPC_INVALID_MALIAS_FORMAT: &str = "Invalid manifest alias format";
#[cfg(not(feature = "interactive"))]
pub const ERR_RPC_NOT_SUPPORT_IA_MODE: &str = "Not support interactive mode.";
pub const ERR_RPC_FAIL_FORK: &str = "Fails to execute fork.";
pub const ERR_RPC_BUFFER_EXCEED: &str = "Stdin buffer size exceeds {} bytes.";
pub const ERR_RPC_INVALID_COMMAND: &str = "Command '{}' doesn't start with a capital letter.";
pub const ERR_RPC_SYSTEM_ERROR: &str = "System error, errno: {}.";
pub const ERR_IPC_INVALID_REQ_FORMAT: &str = "Invalid request struct format.";
pub const ERR_IPC_NOT_SUPPORT_REQ: &str = "Not support request.";
pub const ERR_RPC_NOT_SUPPORT_IA_MODE: &str = "Interactive mode not supported";
pub const ERR_RPC_FAIL_FORK: &str = "Fail to execute fork";
pub const ERR_RPC_BUFFER_EXCEED: &str = "Stdin buffer size exceeds capture size";
pub const ERR_RPC_INVALID_COMMAND: &str = "Command not start with a capital letter";
pub const ERR_RPC_SYSTEM_ERROR: &str = "System error, errno: {}";
pub const ERR_IPC_INVALID_REQ_FORMAT: &str = "Invalid request struct format";
pub const ERR_IPC_NOT_SUPPORT_REQ: &str = "Request not supported";

const ATTEST_DEV_PATH: &str = "/dev/tdx_guest";
const STORAGE_ROOT: &str = "/run/acond";
Expand Down

0 comments on commit 3806f81

Please sign in to comment.