use anyhow::{bail, Result};
use std::process::{Command, ExitStatus, Stdio};
use std::io::{BufRead, BufReader};
use std::sync::mpsc;
use std::thread;
use crate::error::RustpmError;
use super::detector::running_kernel;
fn stream_command(
program: &str,
args: &[&str],
tx: &mpsc::Sender<String>,
) -> Result<ExitStatus> {
let mut child = Command::new(program)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let stdout = child.stdout.take().unwrap();
let stderr = child.stderr.take().unwrap();
let tx1 = tx.clone();
let tx2 = tx.clone();
let t1 = thread::spawn(move || {
for line in BufReader::new(stdout).lines().map_while(Result::ok) {
let _ = tx1.send(line);
}
});
let t2 = thread::spawn(move || {
for line in BufReader::new(stderr).lines().map_while(Result::ok) {
let _ = tx2.send(line);
}
});
let status = child.wait()?;
let _ = t1.join();
let _ = t2.join();
Ok(status)
}
pub fn install_kernel(version: &str, tx: &mpsc::Sender<String>) -> Result<()> {
let image = format!("linux-image-{}", version);
let headers = format!("linux-headers-{}", version);
let _ = tx.send(format!("Installing {} and {} ...", image, headers));
let status = stream_command(
"apt-get",
&["install", "-y", &image, &headers],
tx,
)?;
if !status.success() {
bail!("apt-get install failed with status: {}", status);
}
trigger_grub_update(tx)?;
Ok(())
}
pub fn remove_kernel(version: &str, tx: &mpsc::Sender<String>) -> Result<()> {
let running = running_kernel();
if version == running {
return Err(RustpmError::RunningKernel(version.to_string()).into());
}
let image = format!("linux-image-{}", version);
let headers = format!("linux-headers-{}", version);
let _ = tx.send(format!("Removing {} and {} ...", image, headers));
let status = stream_command(
"apt-get",
&["remove", "-y", &image, &headers],
tx,
)?;
if !status.success() {
bail!("apt-get remove failed with status: {}", status);
}
trigger_grub_update(tx)?;
Ok(())
}
pub fn update_kernel(tx: &mpsc::Sender<String>) -> Result<()> {
let _ = tx.send("Upgrading kernel packages...".into());
let status = stream_command(
"apt-get",
&["install", "-y", "--only-upgrade", "linux-image-amd64", "linux-headers-amd64"],
tx,
)?;
if !status.success() {
bail!("kernel upgrade failed");
}
trigger_grub_update(tx)?;
Ok(())
}
pub fn pin_kernel(version: &str) -> Result<()> {
let image = format!("linux-image-{}", version);
let headers = format!("linux-headers-{}", version);
let status = Command::new("apt-mark")
.args(["hold", &image, &headers])
.status()?;
if !status.success() {
bail!("apt-mark hold failed");
}
Ok(())
}
pub fn unpin_kernel(version: &str) -> Result<()> {
let image = format!("linux-image-{}", version);
let headers = format!("linux-headers-{}", version);
let status = Command::new("apt-mark")
.args(["unhold", &image, &headers])
.status()?;
if !status.success() {
bail!("apt-mark unhold failed");
}
Ok(())
}
pub fn trigger_grub_update(tx: &mpsc::Sender<String>) -> Result<()> {
let _ = tx.send("Updating GRUB...".into());
let status = stream_command("update-grub", &[], tx)?;
if !status.success() {
let _ = tx.send("Warning: update-grub returned non-zero exit code".into());
}
Ok(())
}