Skip to content

Commit a8358b6

Browse files
committed
Support temporary data directories and files.
Create and robustly delete temporary data, which is stored in the user data directory under `${data_dir}/cross-rs/tmp`. Therefore, if program execution is ever halted, deleting the directory will automatically cleanup any remaining data. A termination handler cleans up the temporary data on exit by clearing a stack of temporary files/directories, and we have 2 resource handlers that cleanup the temporary data when the object is dropped. The new installer for the startup hooks is `install_exit_hooks`, which installs both the panic and termination handlers. To get the directory containing all temporary data, use `cross::temp::dir()`. An example of using temporary directories and files is: ```rust { // these are all safe due to single-threaded execution let tmpdir1 = unsafe { temp::TempFile::new() }?; let tmpdir2 = unsafe { temp::TempFile::new() }?; let tmpfile1 = unsafe { temp::TempFile::new() }?; let tmpfile2 = unsafe { temp::TempFile::new() }?; for entry in fs::read_dir(tmpdir1.path()) { ... } for entry in fs::read_dir(tmpdir2.path()) { ... } } // cleanup tmpfile2 -> tmpfile1 -> tmpdir2 -> tmpdir1 ``` Note that only these 2 wrappers are provided, since it guarantees the temporary file and directory stack stays ordered and resources are cleaned up as desired.
1 parent 05c4490 commit a8358b6

File tree

9 files changed

+322
-1
lines changed

9 files changed

+322
-1
lines changed

Cargo.lock

Lines changed: 111 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ serde_json = "1"
4343
serde_ignored = "0.1.2"
4444
shell-words = "1.1.0"
4545
const-sha1 = "0.2.0"
46+
ctrlc = { version = "3.2.2", features = ["termination"] }
47+
directories = "4.0.1"
48+
tempfile = "3.3.0"
4649

4750
[target.'cfg(not(windows))'.dependencies]
4851
nix = { version = "0.24", default-features = false, features = ["user"] }

src/bin/commands/clean.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::fs;
2+
3+
use super::images::RemoveImages;
4+
use clap::Args;
5+
6+
#[derive(Args, Debug)]
7+
pub struct Clean {
8+
/// Provide verbose diagnostic output.
9+
#[clap(short, long)]
10+
pub verbose: bool,
11+
/// Force removal of images.
12+
#[clap(short, long)]
13+
pub force: bool,
14+
/// Remove local (development) images.
15+
#[clap(short, long)]
16+
pub local: bool,
17+
/// Remove images. Default is a dry run.
18+
#[clap(short, long)]
19+
pub execute: bool,
20+
/// Container engine (such as docker or podman).
21+
#[clap(long)]
22+
pub engine: Option<String>,
23+
}
24+
25+
impl Clean {
26+
pub fn run(self, engine: cross::docker::Engine) -> cross::Result<()> {
27+
let tempdir = cross::temp::dir()?;
28+
match self.execute {
29+
true => {
30+
if tempdir.exists() {
31+
fs::remove_dir_all(tempdir)?;
32+
}
33+
}
34+
false => println!("fs::remove_dir_all({})", tempdir.display()),
35+
}
36+
37+
let remove_images = RemoveImages {
38+
targets: vec![],
39+
verbose: self.verbose,
40+
force: self.force,
41+
local: self.local,
42+
execute: self.execute,
43+
engine: None,
44+
};
45+
remove_images.run(engine)?;
46+
47+
Ok(())
48+
}
49+
}

src/bin/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod clean;
12
mod images;
23

4+
pub use self::clean::*;
35
pub use self::images::*;

src/bin/cross-util.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ enum Commands {
1919
/// List cross images in local storage.
2020
#[clap(subcommand)]
2121
Images(commands::Images),
22+
/// Clean all cross data in local storage.
23+
Clean(commands::Clean),
2224
}
2325

2426
fn is_toolchain(toolchain: &str) -> cross::Result<String> {
@@ -49,6 +51,10 @@ pub fn main() -> cross::Result<()> {
4951
let engine = get_container_engine(args.engine(), args.verbose())?;
5052
args.run(engine)?;
5153
}
54+
Commands::Clean(args) => {
55+
let engine = get_container_engine(args.engine.as_deref(), args.verbose)?;
56+
args.run(engine)?;
57+
}
5258
}
5359

5460
Ok(())

src/bin/cross.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
pub fn main() -> cross::Result<()> {
44
cross::install_panic_hook()?;
5+
cross::install_termination_hook()?;
6+
57
let status = cross::run()?;
68
let code = status
79
.code()

src/errors.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
1+
use crate::temp;
2+
3+
use std::sync::atomic::{AtomicBool, Ordering};
4+
15
pub use color_eyre::Section;
26
pub use eyre::Context;
37
pub use eyre::Result;
48

9+
pub static mut TERMINATED: AtomicBool = AtomicBool::new(false);
10+
511
pub fn install_panic_hook() -> Result<()> {
612
color_eyre::config::HookBuilder::new()
713
.display_env_section(false)
814
.install()
915
}
1016

17+
/// # Safety
18+
/// Safe as long as we have single-threaded execution.
19+
unsafe fn termination_handler() {
20+
// we can't warn the user here, since locks aren't signal-safe.
21+
// we can delete files, since fdopendir is thread-safe, and
22+
// `openat`, `unlinkat`, and `lstat` are signal-safe.
23+
// https://man7.org/linux/man-pages/man7/signal-safety.7.html
24+
if !TERMINATED.swap(true, Ordering::SeqCst) && temp::has_tempfiles() {
25+
temp::clean();
26+
}
27+
28+
// EOWNERDEAD, seems to be the same on linux, macos, and bash on windows.
29+
std::process::exit(130);
30+
}
31+
32+
pub fn install_termination_hook() -> Result<()> {
33+
// SAFETY: safe since single-threaded execution.
34+
ctrlc::set_handler(|| unsafe { termination_handler() }).map_err(Into::into)
35+
}
36+
1137
#[derive(Debug, thiserror::Error)]
1238
pub enum CommandError {
1339
#[error("`{command}` failed with {status}")]

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod id;
2929
mod interpreter;
3030
mod rustc;
3131
mod rustup;
32+
pub mod temp;
3233

3334
use std::env;
3435
use std::io::{self, Write};
@@ -44,7 +45,7 @@ use self::cross_toml::CrossToml;
4445
use self::errors::Context;
4546
use self::rustc::{TargetList, VersionMetaExt};
4647

47-
pub use self::errors::{install_panic_hook, Result};
48+
pub use self::errors::{install_panic_hook, install_termination_hook, Result};
4849
pub use self::extensions::{CommandExt, OutputExt};
4950
pub use self::file::ToUtf8;
5051

0 commit comments

Comments
 (0)