Skip to content

Commit 7afbfe9

Browse files
Merge #1071
1071: Terminate child containers on program termination. r=Emilgardis a=Alexhuszagh Upon receiving a termination signal, ensuring any spawned containers are also terminated. This is required since we intentional do not attach stdint to the container. This means, however, that interrupts such as Ctrl+C did not stop the build process. Likewise, termination signals such as SIGTERM merely exited cross, and did not terminate the container. This also refactors the global container state to store whether the container exists and current logic to terminate the container if it exists. By coupling the two, it simplifies safe handling and modification of this global state. Since we have a signal handler, these functions may be called asynchronously, therefore any global state may be modified, rendering the current state invalid if not appropriately guarded. Coupling the two ensures that our logic is sound. Fixes the regression in #964. Closes #1041. Co-authored-by: Alex Huszagh <ahuszagh@gmail.com>
2 parents e2c2180 + b933188 commit 7afbfe9

File tree

6 files changed

+325
-245
lines changed

6 files changed

+325
-245
lines changed

src/bin/commands/containers.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ fn get_cross_volumes(
319319
engine: &docker::Engine,
320320
msg_info: &mut MessageInfo,
321321
) -> cross::Result<Vec<String>> {
322-
use cross::docker::remote::VOLUME_PREFIX;
322+
use cross::docker::VOLUME_PREFIX;
323323
let stdout = docker::subcommand(engine, "volume")
324324
.arg("list")
325325
.args(["--format", "{{.Name}}"])
@@ -400,7 +400,7 @@ pub fn create_persistent_volume(
400400
let container = dirs.unique_container_identifier(&toolchain.host().target)?;
401401
let volume = dirs.unique_toolchain_identifier()?;
402402

403-
if docker::remote::volume_exists(engine, &volume, msg_info)? {
403+
if docker::volume_exists(engine, &volume, msg_info)? {
404404
eyre::bail!("Error: volume {volume} already exists.");
405405
}
406406

@@ -409,18 +409,18 @@ pub fn create_persistent_volume(
409409
.run_and_get_status(msg_info, false)?;
410410

411411
// stop the container if it's already running
412-
let state = docker::remote::container_state(engine, &container, msg_info)?;
412+
let state = docker::container_state(engine, &container, msg_info)?;
413413
if !state.is_stopped() {
414414
msg_info.warn(format_args!("container {container} was running."))?;
415-
docker::remote::container_stop_default(engine, &container, msg_info)?;
415+
docker::container_stop_default(engine, &container, msg_info)?;
416416
}
417417
if state.exists() {
418418
msg_info.warn(format_args!("container {container} was exited."))?;
419-
docker::remote::container_rm(engine, &container, msg_info)?;
419+
docker::container_rm(engine, &container, msg_info)?;
420420
}
421421

422422
// create a dummy running container to copy data over
423-
let mount_prefix = docker::remote::MOUNT_PREFIX;
423+
let mount_prefix = docker::MOUNT_PREFIX;
424424
let mut docker = docker::subcommand(engine, "run");
425425
docker.args(["--name", &container]);
426426
docker.arg("--rm");
@@ -440,7 +440,7 @@ pub fn create_persistent_volume(
440440
docker.args(["sh", "-c", "sleep infinity"]);
441441
}
442442
// store first, since failing to non-existing container is fine
443-
docker::remote::create_container_deleter(engine.clone(), container.clone());
443+
docker::Container::create(engine.clone(), container.clone())?;
444444
docker.run_and_get_status(msg_info, false)?;
445445

446446
docker::remote::copy_volume_container_xargo(
@@ -467,7 +467,7 @@ pub fn create_persistent_volume(
467467
msg_info,
468468
)?;
469469

470-
docker::remote::drop_container(is_tty, msg_info);
470+
docker::Container::finish_static(is_tty, msg_info);
471471

472472
Ok(())
473473
}
@@ -486,11 +486,11 @@ pub fn remove_persistent_volume(
486486
let dirs = docker::ToolchainDirectories::assemble(&mount_finder, toolchain)?;
487487
let volume = dirs.unique_toolchain_identifier()?;
488488

489-
if !docker::remote::volume_exists(engine, &volume, msg_info)? {
489+
if !docker::volume_exists(engine, &volume, msg_info)? {
490490
eyre::bail!("Error: volume {volume} does not exist.");
491491
}
492492

493-
docker::remote::volume_rm(engine, &volume, msg_info)?;
493+
docker::volume_rm(engine, &volume, msg_info)?;
494494

495495
Ok(())
496496
}
@@ -499,7 +499,7 @@ fn get_cross_containers(
499499
engine: &docker::Engine,
500500
msg_info: &mut MessageInfo,
501501
) -> cross::Result<Vec<String>> {
502-
use cross::docker::remote::VOLUME_PREFIX;
502+
use cross::docker::VOLUME_PREFIX;
503503
let stdout = docker::subcommand(engine, "ps")
504504
.arg("-a")
505505
.args(["--format", "{{.Names}}: {{.State}}"])
@@ -533,7 +533,7 @@ pub fn remove_all_containers(
533533
// cannot fail, formatted as {{.Names}}: {{.State}}
534534
let (name, state) = container.split_once(':').unwrap();
535535
let name = name.trim();
536-
let state = docker::remote::ContainerState::new(state.trim())?;
536+
let state = docker::ContainerState::new(state.trim())?;
537537
if state.is_stopped() {
538538
stopped.push(name);
539539
} else {

src/docker/local.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::io;
22
use std::path::Path;
33
use std::process::{Command, ExitStatus};
4+
use std::sync::atomic::Ordering;
45

56
use super::shared::*;
67
use crate::errors::Result;
@@ -50,6 +51,8 @@ pub(crate) fn run(
5051
msg_info,
5152
)?;
5253

54+
let container = toolchain_dirs.unique_container_identifier(options.target.target())?;
55+
docker.args(["--name", &container]);
5356
docker.arg("--rm");
5457

5558
docker_seccomp(&mut docker, engine.kind, &options.target, &paths.metadata)
@@ -121,9 +124,22 @@ pub(crate) fn run(
121124
.wrap_err("when building custom image")?;
122125
}
123126

124-
docker
127+
Container::create(engine.clone(), container)?;
128+
let status = docker
125129
.arg(&image_name)
126130
.args(["sh", "-c", &build_command(toolchain_dirs, &cmd)])
127131
.run_and_get_status(msg_info, false)
128-
.map_err(Into::into)
132+
.map_err(Into::into);
133+
134+
// `cargo` generally returns 0 or 101 on completion, but isn't guaranteed
135+
// to. `ExitStatus::code()` may be None if a signal caused the process to
136+
// terminate or it may be a known interrupt return status (130, 137, 143).
137+
// simpler: just test if the program termination handler was called.
138+
// SAFETY: an atomic load.
139+
let is_terminated = unsafe { crate::errors::TERMINATED.load(Ordering::SeqCst) };
140+
if !is_terminated {
141+
Container::exit_static();
142+
}
143+
144+
status
129145
}

src/docker/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pub mod custom;
1+
pub(crate) mod custom;
22
mod engine;
33
mod image;
44
mod local;

0 commit comments

Comments
 (0)