fix: Support other signals properly (#194)
This commit is contained in:
parent
d0d51d5aa0
commit
7a563f42da
11 changed files with 175 additions and 126 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -287,14 +287,13 @@ dependencies = [
|
|||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"ctrlc",
|
||||
"directories",
|
||||
"format_serde_error",
|
||||
"indicatif",
|
||||
"indicatif-log-bridge",
|
||||
"log",
|
||||
"log4rs",
|
||||
"nix 0.29.0",
|
||||
"nix",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"os_pipe",
|
||||
|
|
@ -303,6 +302,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml 0.9.34+deprecated",
|
||||
"signal-hook",
|
||||
"syntect",
|
||||
"typed-builder",
|
||||
"which",
|
||||
|
|
@ -331,12 +331,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
|
|
@ -567,16 +561,6 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
|
||||
dependencies = [
|
||||
"nix 0.28.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
|
|
@ -1182,18 +1166,6 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.1.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
|
|
@ -1202,7 +1174,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
|||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
|
@ -1777,6 +1749,7 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
|
|
|||
8
Dockerfile.alpine
Normal file
8
Dockerfile.alpine
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
RUN apk update && apk add buildah podman skopeo fuse-overlayfs gpg tini dumb-init
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
|
||||
STOPSIGNAL SIGTERM
|
||||
20
Dockerfile.fedora
Normal file
20
Dockerfile.fedora
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
ARG BASE_IMAGE="registry.fedoraproject.org/fedora-toolbox"
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
RUN dnf -y install dnf-plugins-core \
|
||||
&& dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo \
|
||||
&& dnf install --refresh -y \
|
||||
docker-ce \
|
||||
docker-ce-cli \
|
||||
containerd.io \
|
||||
docker-buildx-plugin \
|
||||
docker-compose-plugin \
|
||||
buildah \
|
||||
podman \
|
||||
skopeo \
|
||||
gpg \
|
||||
dumb-init
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
|
||||
STOPSIGNAL SIGTERM
|
||||
22
Earthfile
22
Earthfile
|
|
@ -92,24 +92,12 @@ build-scripts:
|
|||
|
||||
blue-build-cli-prebuild:
|
||||
ARG BASE_IMAGE="registry.fedoraproject.org/fedora-toolbox"
|
||||
FROM $BASE_IMAGE
|
||||
FROM DOCKERFILE -f Dockerfile.fedora .
|
||||
|
||||
COPY (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
RUN dnf -y install dnf-plugins-core \
|
||||
&& dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo \
|
||||
&& dnf install --refresh -y \
|
||||
docker-ce \
|
||||
docker-ce-cli \
|
||||
containerd.io \
|
||||
docker-buildx-plugin \
|
||||
docker-compose-plugin \
|
||||
buildah \
|
||||
podman \
|
||||
skopeo
|
||||
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
|
|
@ -128,20 +116,18 @@ blue-build-cli:
|
|||
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
ENTRYPOINT ["bluebuild"]
|
||||
CMD ["bluebuild"]
|
||||
|
||||
DO --pass-args +SAVE_IMAGE
|
||||
|
||||
blue-build-cli-alpine-prebuild:
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
FROM DOCKERFILE -f Dockerfile.alpine .
|
||||
|
||||
COPY (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
RUN apk update && apk add buildah podman skopeo fuse-overlayfs
|
||||
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
|
||||
ARG EARTHLY_GIT_HASH
|
||||
|
|
@ -161,7 +147,7 @@ blue-build-cli-alpine:
|
|||
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
ENTRYPOINT ["bluebuild"]
|
||||
CMD ["bluebuild"]
|
||||
|
||||
DO --pass-args +SAVE_IMAGE --SUFFIX="-alpine"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use blue_build::commands::{BlueBuildArgs, BlueBuildCommand, CommandArgs};
|
||||
use blue_build_utils::{ctrlc_handler, logging::Logger};
|
||||
use blue_build_utils::{logging::Logger, signal_handler};
|
||||
use clap::Parser;
|
||||
use log::LevelFilter;
|
||||
|
||||
|
|
@ -11,12 +11,9 @@ fn main() {
|
|||
.filter_modules([("hyper::proto", LevelFilter::Info)])
|
||||
.log_out_dir(args.log_out.clone())
|
||||
.init();
|
||||
|
||||
ctrlc_handler::init();
|
||||
|
||||
log::trace!("Parsed arguments: {args:#?}");
|
||||
|
||||
match args.command {
|
||||
signal_handler::init(|| match args.command {
|
||||
#[cfg(feature = "init")]
|
||||
CommandArgs::Init(mut command) => command.run(),
|
||||
|
||||
|
|
@ -39,5 +36,5 @@ fn main() {
|
|||
CommandArgs::BugReport(mut command) => command.run(),
|
||||
|
||||
CommandArgs::Completions(mut command) => command.run(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub trait BlueBuildCommand {
|
|||
error!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ license.workspace = true
|
|||
atty = "0.2"
|
||||
base64 = "0.22.1"
|
||||
blake2 = "0.10.6"
|
||||
ctrlc = { version = "3.4.4", features = ["termination"] }
|
||||
directories = "5"
|
||||
rand = "0.8.5"
|
||||
log4rs = { version = "1.3.0", features = ["background_rotation"] }
|
||||
|
|
@ -20,6 +19,7 @@ nix = { version = "0.29.0", features = ["signal"] }
|
|||
nu-ansi-term = { version = "0.50.0", features = ["gnu_legacy"] }
|
||||
os_pipe = { version = "1", features = ["io_safety"] }
|
||||
process_control = { version = "4", features = ["crossbeam-channel"] }
|
||||
signal-hook = { version = "0.3.17", features = ["extended-siginfo"] }
|
||||
syntect = "5"
|
||||
which = "6"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
use std::{
|
||||
process,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use nix::{
|
||||
sys::signal::{kill, Signal::SIGTERM},
|
||||
unistd::Pid,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
static PID_LIST: Lazy<Arc<Mutex<Vec<i32>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
|
||||
|
||||
/// Initialize Ctrl-C handler. This should be done at the start
|
||||
/// of a binary.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if initialized more than once.
|
||||
pub fn init() {
|
||||
let pid_list = PID_LIST.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
let pid_list = pid_list.lock().expect("Should lock mutex");
|
||||
pid_list.iter().for_each(|pid| {
|
||||
if let Err(e) = kill(Pid::from_raw(*pid), SIGTERM) {
|
||||
error!("Failed to kill process {pid}: Error {e}");
|
||||
}
|
||||
});
|
||||
drop(pid_list);
|
||||
process::exit(1);
|
||||
})
|
||||
.expect("Should create ctrlc handler");
|
||||
}
|
||||
|
||||
/// Add a pid to the list to kill when the program
|
||||
/// recieves a kill signal.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if the mutex cannot be locked.
|
||||
pub fn add_pid<T>(pid: T)
|
||||
where
|
||||
T: TryInto<i32>,
|
||||
{
|
||||
if let Ok(pid) = pid.try_into() {
|
||||
let mut pid_list = PID_LIST.lock().expect("Should lock pid_list");
|
||||
|
||||
if !pid_list.contains(&pid) {
|
||||
pid_list.push(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a pid from the list of pids to kill.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if the mutex cannot be locked.
|
||||
pub fn remove_pid<T>(pid: T)
|
||||
where
|
||||
T: TryInto<i32>,
|
||||
{
|
||||
if let Ok(pid) = pid.try_into() {
|
||||
let mut pid_list = PID_LIST.lock().expect("Should lock pid_list");
|
||||
|
||||
if let Some(index) = pid_list.iter().position(|val| *val == pid) {
|
||||
pid_list.swap_remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
pub mod command_output;
|
||||
pub mod constants;
|
||||
pub mod ctrlc_handler;
|
||||
pub mod logging;
|
||||
pub mod signal_handler;
|
||||
pub mod syntax_highlighting;
|
||||
|
||||
use std::{
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use once_cell::sync::Lazy;
|
|||
use rand::Rng;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use crate::ctrlc_handler::{add_pid, remove_pid};
|
||||
use crate::signal_handler::{add_pid, remove_pid};
|
||||
|
||||
static MULTI_PROGRESS: Lazy<MultiProgress> = Lazy::new(MultiProgress::new);
|
||||
static LOG_DIR: Lazy<Mutex<PathBuf>> = Lazy::new(|| Mutex::new(PathBuf::new()));
|
||||
|
|
|
|||
132
utils/src/signal_handler.rs
Normal file
132
utils/src/signal_handler.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
use std::{
|
||||
process,
|
||||
sync::{atomic::AtomicBool, Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use log::{error, trace, warn};
|
||||
use nix::{
|
||||
libc::{SIGABRT, SIGCONT, SIGHUP, SIGTSTP},
|
||||
sys::signal::{kill, Signal},
|
||||
unistd::Pid,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use signal_hook::{
|
||||
consts::TERM_SIGNALS,
|
||||
flag,
|
||||
iterator::{exfiltrator::WithOrigin, SignalsInfo},
|
||||
low_level,
|
||||
};
|
||||
|
||||
use crate::logging::Logger;
|
||||
|
||||
static PID_LIST: Lazy<Arc<Mutex<Vec<i32>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
|
||||
|
||||
/// Initialize Ctrl-C handler. This should be done at the start
|
||||
/// of a binary.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if initialized more than once.
|
||||
pub fn init<F>(app_exec: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
// Make sure double CTRL+C and similar kills
|
||||
let term_now = Arc::new(AtomicBool::new(false));
|
||||
for sig in TERM_SIGNALS {
|
||||
// When terminated by a second term signal, exit with exit code 1.
|
||||
// This will do nothing the first time (because term_now is false).
|
||||
flag::register_conditional_shutdown(*sig, 1, Arc::clone(&term_now))
|
||||
.expect("Register conditional shutdown");
|
||||
// But this will "arm" the above for the second time, by setting it to true.
|
||||
// The order of registering these is important, if you put this one first, it will
|
||||
// first arm and then terminate ‒ all in the first round.
|
||||
flag::register(*sig, Arc::clone(&term_now)).expect("Register signal");
|
||||
}
|
||||
|
||||
let mut signals = vec![SIGABRT, SIGHUP, SIGTSTP, SIGCONT];
|
||||
signals.extend(TERM_SIGNALS);
|
||||
let mut signals = SignalsInfo::<WithOrigin>::new(signals).expect("Need signal info");
|
||||
|
||||
thread::spawn(app_exec);
|
||||
|
||||
let mut has_terminal = true;
|
||||
for info in &mut signals {
|
||||
match info.signal {
|
||||
termsig if TERM_SIGNALS.contains(&termsig) => {
|
||||
warn!("Received termination signal, cleaning up...");
|
||||
trace!("{info:#?}");
|
||||
|
||||
Logger::multi_progress().clear().unwrap();
|
||||
|
||||
send_signal_processes(termsig);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
SIGTSTP => {
|
||||
if has_terminal {
|
||||
send_signal_processes(SIGTSTP);
|
||||
has_terminal = false;
|
||||
low_level::emulate_default_handler(SIGTSTP).expect("Should stop");
|
||||
}
|
||||
}
|
||||
SIGCONT => {
|
||||
if !has_terminal {
|
||||
send_signal_processes(SIGCONT);
|
||||
has_terminal = true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
trace!("Received signal {info:#?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_signal_processes(sig: i32) {
|
||||
let pid_list = PID_LIST.clone();
|
||||
let pid_list = pid_list.lock().expect("Should lock mutex");
|
||||
pid_list.iter().for_each(|pid| {
|
||||
if let Err(e) = kill(Pid::from_raw(*pid), Signal::try_from(sig).unwrap()) {
|
||||
error!("Failed to kill process {pid}: Error {e}");
|
||||
} else {
|
||||
trace!("Killed process {pid}");
|
||||
}
|
||||
});
|
||||
drop(pid_list);
|
||||
}
|
||||
|
||||
/// Add a pid to the list to kill when the program
|
||||
/// recieves a kill signal.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if the mutex cannot be locked.
|
||||
pub fn add_pid<T>(pid: T)
|
||||
where
|
||||
T: TryInto<i32>,
|
||||
{
|
||||
if let Ok(pid) = pid.try_into() {
|
||||
let mut pid_list = PID_LIST.lock().expect("Should lock pid_list");
|
||||
|
||||
if !pid_list.contains(&pid) {
|
||||
pid_list.push(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a pid from the list of pids to kill.
|
||||
///
|
||||
/// # Panics
|
||||
/// Will panic if the mutex cannot be locked.
|
||||
pub fn remove_pid<T>(pid: T)
|
||||
where
|
||||
T: TryInto<i32>,
|
||||
{
|
||||
if let Ok(pid) = pid.try_into() {
|
||||
let mut pid_list = PID_LIST.lock().expect("Should lock pid_list");
|
||||
|
||||
if let Some(index) = pid_list.iter().position(|val| *val == pid) {
|
||||
pid_list.swap_remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue