From c2362e1cab48af81e1c5baea26ffb28e543b1ed9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 29 Oct 2021 11:52:27 -0400 Subject: [PATCH] Handle case where skopeo exits unexpectedly Confusingly, today running `skopeo some-unknown-command` just outputs usage information to stdout, and exits successfully. I'll probably fix that to exit with an error, but either way we need to handle the case where skopeo exits before we get a reply from the socketpair. --- src/imageproxy.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/imageproxy.rs b/src/imageproxy.rs index fcf44b4..07260a3 100644 --- a/src/imageproxy.rs +++ b/src/imageproxy.rs @@ -13,7 +13,7 @@ use std::fs::File; use std::os::unix::io::AsRawFd; use std::os::unix::prelude::{FromRawFd, RawFd}; use std::pin::Pin; -use std::process::Stdio; +use std::process::{ExitStatus, Stdio}; use std::sync::{Arc, Mutex}; use tokio::io::{AsyncBufRead, AsyncReadExt}; @@ -69,9 +69,9 @@ type JoinFuture = Pin>>>>; /// Manage a child process proxy to fetch container images. pub struct ImageProxy { - proc: tokio::process::Child, sockfd: Arc>, stderr: JoinFuture, + procwait: Pin>>>, } impl std::fmt::Debug for ImageProxy { @@ -130,12 +130,22 @@ impl ImageProxy { .map_err(anyhow::Error::msg) .boxed(); + let mut procwait = Box::pin(async move { proc.wait().map_err(anyhow::Error::msg).await }); + let sockfd = Arc::new(Mutex::new(mysock)); // Verify semantic version - let (protover, _) = - Self::impl_request_raw::(Arc::clone(&sockfd), Request::new_bare("Initialize")) - .await?; + let protoreq = + Self::impl_request_raw::(Arc::clone(&sockfd), Request::new_bare("Initialize")); + let protover = tokio::select! { + r = protoreq => { + r?.0 + } + r = &mut procwait => { + let errmsg = stderr.await??; + return Err(anyhow!("skopeo exited unexpectedly (no support for `experimental-image-proxy`?): {}\n{}", r?, errmsg)); + } + }; let protover = semver::Version::parse(protover.as_str())?; let supported = &*SUPPORTED_PROTO_VERSION; if !supported.matches(&protover) { @@ -147,9 +157,9 @@ impl ImageProxy { } let r = Self { - proc, stderr, sockfd, + procwait, }; Ok(r) } @@ -276,14 +286,14 @@ impl ImageProxy { } /// Close the connection and wait for the child process to exit successfully. - pub async fn finalize(mut self) -> Result<()> { + pub async fn finalize(self) -> Result<()> { let req = Request::new_bare("Shutdown"); let sendbuf = serde_json::to_vec(&req)?; // SAFETY: Only panics if a worker thread already panic'd let sockfd = Arc::try_unwrap(self.sockfd).unwrap().into_inner().unwrap(); nixsocket::send(sockfd.as_raw_fd(), &sendbuf, nixsocket::MsgFlags::empty())?; drop(sendbuf); - let status = self.proc.wait().await?; + let status = self.procwait.await?; if !status.success() { if let Some(stderr) = self.stderr.await.map(|v| v.ok()).ok().flatten() { anyhow::bail!("proxy failed: {}\n{}", status, stderr)