Skip to content

Commit

Permalink
Drop systemd service
Browse files Browse the repository at this point in the history
Fixes #551

Get hints by #551 (comment),
and copy the comment here:
Basically we detect if we're running in systemd; if we're not,
we re-exec ourselves via systemd-run. Then we can just directly
run code in what is now the daemon.

I think an important aspect of this is that we retain something
like `--unit bootupd` which acts as a lock - only one unit with
that name can run at a time to avoid two concurrent invocations
breaking things.
  • Loading branch information
HuijingHei committed May 27, 2024
1 parent d9edd85 commit 6d217ac
Show file tree
Hide file tree
Showing 14 changed files with 69 additions and 422 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
device=$(losetup --list --noheadings --output NAME,BACK-FILE | grep myimage.raw | awk '{print $1}')
sudo mount "${device}p2" /mnt/
sudo ls /mnt/EFI/centos/{grub.cfg,shimx64.efi}
sudo umount /mnt
sudo losetup -D "${device}"
sudo rm -f myimage.raw
- name: bootc install to filesystem
Expand Down
12 changes: 2 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ ifeq ($(CONTAINER_RUNTIME), podman)
IMAGE_PREFIX = localhost/
endif

units = $(addprefix systemd/, bootupd.service bootupd.socket)

.PHONY: all
all: $(units)
all:
cargo build ${CARGO_ARGS}
ln -f target/${PROFILE}/bootupd target/${PROFILE}/bootupctl

Expand All @@ -33,17 +31,11 @@ create-build-container:
build-in-container: create-build-container
${CONTAINER_RUNTIME} run -ti --rm -v .:/srv/bootupd:z ${IMAGE_PREFIX}${IMAGE_NAME} make

.PHONY: install-units
install-units: $(units)
for unit in $(units); do install -D -m 644 --target-directory=$(DESTDIR)$(PREFIX)/lib/systemd/system/ $$unit; done

.PHONY: install
install: install-units
install:
mkdir -p "${DESTDIR}$(PREFIX)/bin" "${DESTDIR}$(LIBEXECDIR)"
install -D -t "${DESTDIR}$(LIBEXECDIR)" target/${PROFILE}/bootupd
ln -f ${DESTDIR}$(LIBEXECDIR)/bootupd ${DESTDIR}$(PREFIX)/bin/bootupctl
install -d "${DESTDIR}$(PREFIX)/lib/systemd/system/multi-user.target.wants"
ln -s ../bootupd.socket "${DESTDIR}$(PREFIX)/lib/systemd/system/multi-user.target.wants"

install-grub-static:
install -m 644 -D -t ${DESTDIR}$(PREFIX)/lib/bootupd/grub2-static src/grub2/*.cfg
Expand Down
9 changes: 0 additions & 9 deletions contrib/packaging/bootupd.spec
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,6 @@ cargo build --release
%make_install INSTALL="install -p -c"
make install-grub-static DESTDIR=%{?buildroot} INSTALL="%{__install} -p"

%post -n %{crate}
%systemd_post bootupd.service bootupd.socket

%preun -n %{crate}
%systemd_preun bootupd.service bootupd.socket

%postun -n %{crate}
%systemd_postun bootupd.service bootupd.socket

%changelog
* Tue Oct 18 2022 Colin Walters <walters@verbum.org> - 0.2.8-3
- Dummy changelog
43 changes: 11 additions & 32 deletions src/bootupd.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))]
use crate::bios;
use crate::component;
use crate::component::{Component, ValidationResult};
use crate::coreos;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
use crate::efi;
use crate::model::{ComponentStatus, ComponentUpdatable, ContentMetadata, SavedState, Status};
use crate::util;
use crate::{component, ipc};
use anyhow::{anyhow, Context, Result};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::path::Path;

/// A message sent from client to server
#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum ClientRequest {
/// Update a component
Update { component: String },
/// Update a component via adoption
AdoptAndUpdate { component: String },
/// Validate a component
Validate { component: String },
/// Print the current state
Status,
}

pub(crate) enum ConfigMode {
None,
Static,
Expand Down Expand Up @@ -408,8 +395,8 @@ pub(crate) fn print_status(status: &Status) -> Result<()> {
Ok(())
}

pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result<()> {
let status: Status = c.send(&ClientRequest::Status)?;
pub(crate) fn client_run_update() -> Result<()> {
let status: Status = status()?;
if status.components.is_empty() && status.adoptable.is_empty() {
println!("No components installed.");
return Ok(());
Expand All @@ -420,9 +407,7 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result
ComponentUpdatable::Upgradable => {}
_ => continue,
};
match c.send(&ClientRequest::Update {
component: name.to_string(),
})? {
match update(name)? {
ComponentUpdateResult::AtLatestVersion => {
// Shouldn't happen unless we raced with another client
eprintln!(
Expand Down Expand Up @@ -450,9 +435,7 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result
}
for (name, adoptable) in status.adoptable.iter() {
if adoptable.confident {
let r: ContentMetadata = c.send(&ClientRequest::AdoptAndUpdate {
component: name.to_string(),
})?;
let r: ContentMetadata = adopt_and_update(name)?;
println!("Adopted and updated: {}: {}", name, r.version);
updated = true;
} else {
Expand All @@ -465,32 +448,28 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result
Ok(())
}

pub(crate) fn client_run_adopt_and_update(c: &mut ipc::ClientToDaemonConnection) -> Result<()> {
let status: Status = c.send(&ClientRequest::Status)?;
pub(crate) fn client_run_adopt_and_update() -> Result<()> {
let status: Status = status()?;
if status.adoptable.is_empty() {
println!("No components are adoptable.");
} else {
for (name, _) in status.adoptable.iter() {
let r: ContentMetadata = c.send(&ClientRequest::AdoptAndUpdate {
component: name.to_string(),
})?;
let r: ContentMetadata = adopt_and_update(name)?;
println!("Adopted and updated: {}: {}", name, r.version);
}
}
Ok(())
}

pub(crate) fn client_run_validate(c: &mut ipc::ClientToDaemonConnection) -> Result<()> {
let status: Status = c.send(&ClientRequest::Status)?;
pub(crate) fn client_run_validate() -> Result<()> {
let status: Status = status()?;
if status.components.is_empty() {
println!("No components installed.");
return Ok(());
}
let mut caught_validation_error = false;
for (name, _) in status.components.iter() {
match c.send(&ClientRequest::Validate {
component: name.to_string(),
})? {
match validate(name)? {
ValidationResult::Valid => {
println!("Validated: {}", name);
}
Expand Down
70 changes: 44 additions & 26 deletions src/cli/bootupctl.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
use crate::bootupd;
use crate::ipc::ClientToDaemonConnection;
use crate::model::Status;
use anyhow::Result;
use clap::Parser;
use log::LevelFilter;

use std::os::unix::process::CommandExt;
use std::process::Command;

static BOOTUPD_ARGS: &[&str] = &[
"--unit",
"bootupd",
"--property",
"WorkingDirectory=/usr",
"--property",
"ProtectHome=yes",
"--property",
"MountFlags=slave",
"bootupctl",
];

/// `bootupctl` sub-commands.
#[derive(Debug, Parser)]
#[clap(name = "bootupctl", about = "Bootupd client application", version)]
Expand Down Expand Up @@ -87,10 +100,8 @@ impl CtlCommand {

/// Runner for `status` verb.
fn run_status(opts: StatusOpts) -> Result<()> {
let mut client = ClientToDaemonConnection::new();
client.connect()?;

let r: Status = client.send(&bootupd::ClientRequest::Status)?;
ensure_running_in_systemd("status")?;
let r = bootupd::status()?;
if opts.json {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
Expand All @@ -101,38 +112,45 @@ impl CtlCommand {
bootupd::print_status(&r)?;
}

client.shutdown()?;
Ok(())
}

/// Runner for `update` verb.
fn run_update() -> Result<()> {
let mut client = ClientToDaemonConnection::new();
client.connect()?;

bootupd::client_run_update(&mut client)?;

client.shutdown()?;
Ok(())
ensure_running_in_systemd("update")?;
bootupd::client_run_update()
}

/// Runner for `update` verb.
fn run_adopt_and_update() -> Result<()> {
let mut client = ClientToDaemonConnection::new();
client.connect()?;

bootupd::client_run_adopt_and_update(&mut client)?;

client.shutdown()?;
Ok(())
ensure_running_in_systemd("adopt-and-update")?;
bootupd::client_run_adopt_and_update()
}

/// Runner for `validate` verb.
fn run_validate() -> Result<()> {
let mut client = ClientToDaemonConnection::new();
client.connect()?;
bootupd::client_run_validate(&mut client)?;
client.shutdown()?;
Ok(())
ensure_running_in_systemd("validate")?;
bootupd::client_run_validate()
}
}

/// Checks if the current process is (apparently at least)
/// running under systemd.
fn running_in_systemd() -> bool {
std::env::var_os("INVOCATION_ID").is_some()
}

/// Detect if we're running in systemd; if we're not, we re-exec ourselves via
/// systemd-run. Then we can just directly run code in what is now the daemon.
fn ensure_running_in_systemd(verb: &str) -> Result<()> {
let running_in_systemd = running_in_systemd();
if !running_in_systemd {
let r = Command::new("systemd-run")
.args(BOOTUPD_ARGS)
.arg(verb)
.exec();
// If we got here, it's always an error
return Err(r.into());
}
Ok(())
}
3 changes: 0 additions & 3 deletions src/cli/bootupd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ impl DCommand {
/// CLI sub-commands.
#[derive(Debug, Parser)]
pub enum DVerb {
#[clap(name = "daemon", about = "Run service logic")]
Daemon,
#[clap(name = "generate-update-metadata", about = "Generate metadata")]
GenerateUpdateMetadata(GenerateOpts),
#[clap(name = "install", about = "Install components")]
Expand Down Expand Up @@ -88,7 +86,6 @@ impl DCommand {
/// Run CLI application.
pub fn run(self) -> Result<()> {
match self.cmd {
DVerb::Daemon => crate::daemon::run(),
DVerb::Install(opts) => Self::run_install(opts),
DVerb::GenerateUpdateMetadata(opts) => Self::run_generate_meta(opts),
}
Expand Down
12 changes: 9 additions & 3 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ mod tests {
#[test]
fn test_multicall_dispatch() {
{
let d_argv = vec!["/usr/bin/bootupd".to_string(), "daemon".to_string()];
let d_argv = vec![
"/usr/bin/bootupd".to_string(),
"generate-update-metadata".to_string(),
];
let cli = MultiCall::from_args(d_argv);
match cli {
MultiCall::Ctl(cmd) => panic!("{:?}", cmd),
Expand All @@ -89,12 +92,15 @@ mod tests {

#[test]
fn test_verbosity() {
let default = MultiCall::from_args(vec!["bootupd".to_string(), "daemon".to_string()]);
let default = MultiCall::from_args(vec![
"bootupd".to_string(),
"generate-update-metadata".to_string(),
]);
assert_eq!(default.loglevel(), LevelFilter::Warn);

let info = MultiCall::from_args(vec![
"bootupd".to_string(),
"daemon".to_string(),
"generate-update-metadata".to_string(),
"-v".to_string(),
]);
assert_eq!(info.loglevel(), LevelFilter::Info);
Expand Down
Loading

0 comments on commit 6d217ac

Please sign in to comment.