Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions crates/system-manager-engine/src/activate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::collections::HashSet;
use std::fs::DirBuilder;
use std::io::Seek;
use std::path::{Path, PathBuf};
use std::time::Duration;
use std::{fs, io, process};
use thiserror::Error;

Expand Down Expand Up @@ -102,7 +103,7 @@ impl StateV1 {
backup_path.add_extension("v0back");
log::info!(
"Create a backup of the v0 state at {}.",
&backup_path.display()
backup_path.display()
);
fs::copy(state_file, backup_path)?;
Ok(filetree.into())
Expand All @@ -128,7 +129,7 @@ impl StateV1 {
}
}

pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
pub fn activate(store_path: &StorePath, ephemeral: bool, timeout: &Option<Duration>) -> Result<()> {
log::info!("Activating system-manager profile: {store_path}");
if ephemeral {
log::info!("Running in ephemeral mode");
Expand All @@ -147,10 +148,10 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
match etc_files::activate(store_path, old_state.file_tree, ephemeral) {
Ok(etc_tree) => {
log::info!("Restarting sysinit-reactivation.target...");
services::restart_sysinit_reactivation_target()?;
services::restart_sysinit_reactivation_target(timeout)?;

// Restart userborn before tmpfiles so users exist when tmpfiles runs
if let Err(e) = services::restart_userborn_if_exists() {
if let Err(e) = services::restart_userborn_if_exists(timeout) {
log::error!("Error restarting userborn.service: {e}");
}

Expand All @@ -164,21 +165,22 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
}

log::info!("Activating systemd services...");
let final_state = match services::activate(store_path, old_state.services, ephemeral) {
Ok(services) => StateV1 {
file_tree: etc_tree,
services,
version: 1,
},
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during activation: {source:?}");
StateV1 {
let final_state =
match services::activate(store_path, old_state.services, ephemeral, timeout) {
Ok(services) => StateV1 {
file_tree: etc_tree,
services: result,
services,
version: 1,
},
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during activation: {source:?}");
StateV1 {
file_tree: etc_tree,
services: result,
version: 1,
}
}
}
};
};
final_state.write_to_file(state_file)?;

if let Err(e) = tmp_result {
Expand Down
8 changes: 4 additions & 4 deletions crates/system-manager-engine/src/activate/etc_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,13 @@ fn create_etc_file(
if old_state.contains(&target) {
log::debug!(
"{} is managed by system-manager. Deleting.",
&target.display()
target.display()
);
fs::remove_file(&target).map_err(|e| ActivationError::WithPartialResult {
result: state.clone(),
source: e.into(),
})?;
log::debug!("{} is managed by system-manager.", &target.display());
log::debug!("{} is managed by system-manager.", target.display());
unix::fs::symlink(file.source.store_path, &target).map_err(|e| {
ActivationError::WithPartialResult {
result: state.clone(),
Expand Down Expand Up @@ -422,7 +422,7 @@ fn find_uid(entry: &EtcFile) -> anyhow::Result<u32> {
|| {
log::warn!(
"Specified user {} not found, defaulting to root",
&entry.user
entry.user
);
0
},
Expand All @@ -443,7 +443,7 @@ fn find_gid(entry: &EtcFile) -> anyhow::Result<u32> {
|| {
log::warn!(
"Specified group {} not found, defaulting to root",
&entry.group
entry.group
);
0
},
Expand Down
21 changes: 9 additions & 12 deletions crates/system-manager-engine/src/activate/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub fn activate(
store_path: &StorePath,
old_services: Services,
ephemeral: bool,
timeout: &Option<Duration>,
) -> ServiceActivationResult {
verify_systemd_dir(ephemeral)
.map_err(|e| ActivationError::with_partial_result(old_services.clone(), e))?;
Expand All @@ -82,7 +83,6 @@ pub fn activate(
let job_monitor = service_manager
.monitor_jobs_init()
.map_err(|e| ActivationError::with_partial_result(old_services.clone(), e))?;
let timeout = Some(Duration::from_secs(30));

// Stop removed services and any masked services that might still be running
// (e.g. distro-provided units). Must happen before daemon-reload so systemd
Expand All @@ -93,7 +93,7 @@ pub fn activate(
&service_manager,
&job_monitor,
stop_services(&service_manager, units_to_stop),
&timeout,
timeout,
)
.map_err(|e| ActivationError::with_partial_result(services.clone(), e))?;

Expand All @@ -102,7 +102,7 @@ pub fn activate(
&job_monitor,
reload_or_restart_units(&service_manager, convert_services(&services_to_reload))
+ start_units(&service_manager, ["system-manager.target"]),
&timeout,
timeout,
)
.map_err(|e| ActivationError::with_partial_result(services.clone(), e))?;

Expand Down Expand Up @@ -228,7 +228,7 @@ fn verify_systemd_dir(ephemeral: bool) -> anyhow::Result<()> {
Ok(())
}

pub fn deactivate(old_services: Services) -> ServiceActivationResult {
pub fn deactivate(old_services: Services, timeout: &Option<Duration>) -> ServiceActivationResult {
log::debug!("{:?}", old_services);

restore_ephemeral_system_dir()
Expand All @@ -247,7 +247,6 @@ pub fn deactivate(old_services: Services) -> ServiceActivationResult {
let job_monitor = service_manager
.monitor_jobs_init()
.map_err(|e| ActivationError::with_partial_result(old_services.clone(), e))?;
let timeout = Some(Duration::from_secs(30));

let mut units_to_stop = convert_services(&stoppable);
units_to_stop.push("system-manager.target");
Expand All @@ -257,7 +256,7 @@ pub fn deactivate(old_services: Services) -> ServiceActivationResult {
&service_manager,
&job_monitor,
stop_services(&service_manager, units_to_stop),
&timeout,
timeout,
)
// We consider all jobs stopped now..
.map_err(|e| ActivationError::with_partial_result(im::HashMap::new(), e))?;
Expand Down Expand Up @@ -397,10 +396,9 @@ impl From<JobId> for String {
}
}

pub fn restart_sysinit_reactivation_target() -> anyhow::Result<()> {
pub fn restart_sysinit_reactivation_target(timeout: &Option<Duration>) -> anyhow::Result<()> {
let service_manager = systemd::ServiceManager::new_session()?;
let job_monitor = service_manager.monitor_jobs_init()?;
let timeout = Some(Duration::from_secs(30));

log::info!("Reloading the systemd daemon...");
service_manager.daemon_reload()?;
Expand All @@ -411,14 +409,14 @@ pub fn restart_sysinit_reactivation_target() -> anyhow::Result<()> {
"restarting",
);

wait_for_jobs(&service_manager, &job_monitor, jobs, &timeout)?;
wait_for_jobs(&service_manager, &job_monitor, jobs, timeout)?;
Ok(())
}

/// This must be called after daemon-reload so systemd knows about the unit,
/// but before tmpfiles activation since tmpfiles may reference users that
/// userborn needs to create.
pub fn restart_userborn_if_exists() -> anyhow::Result<()> {
pub fn restart_userborn_if_exists(timeout: &Option<Duration>) -> anyhow::Result<()> {
let service_manager = systemd::ServiceManager::new_session()?;

// Check if userborn.service exists by listing units matching the pattern
Expand All @@ -431,7 +429,6 @@ pub fn restart_userborn_if_exists() -> anyhow::Result<()> {

log::info!("Restarting userborn.service to create users before tmpfiles...");
let job_monitor = service_manager.monitor_jobs_init()?;
let timeout = Some(Duration::from_secs(30));

// We use restart rather than start because userborn is a oneshot service
// with RemainAfterExit=true.
Expand All @@ -441,7 +438,7 @@ pub fn restart_userborn_if_exists() -> anyhow::Result<()> {
"restarting",
);

wait_for_jobs(&service_manager, &job_monitor, jobs, &timeout)?;
wait_for_jobs(&service_manager, &job_monitor, jobs, timeout)?;
log::info!("userborn.service completed");
Ok(())
}
5 changes: 3 additions & 2 deletions crates/system-manager-engine/src/deactivate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use std::time::Duration;

use crate::activate::etc_files;
use crate::activate::services;
Expand All @@ -7,7 +8,7 @@ use crate::activate::{get_state_file, ActivationError, StateV1};

/// Deactivates system-manager by locking managed users, removing etc files,
/// and stopping systemd services.
pub fn deactivate() -> Result<()> {
pub fn deactivate(timeout: &Option<Duration>) -> Result<()> {
log::info!("Deactivating system-manager");
let state_file = &get_state_file()?;
let old_state = StateV1::from_file(state_file)?;
Expand All @@ -24,7 +25,7 @@ pub fn deactivate() -> Result<()> {
match etc_files::deactivate(old_state.file_tree) {
Ok(etc_tree) => {
log::info!("Deactivating systemd services...");
match services::deactivate(old_state.services) {
match services::deactivate(old_state.services, timeout) {
Ok(services) => StateV1 {
file_tree: etc_tree,
services,
Expand Down
32 changes: 30 additions & 2 deletions crates/system-manager-engine/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use anyhow::Result;
use clap::Parser;
use std::process::ExitCode;
use std::time::Duration;

use system_manager_engine::{NixOptions, StorePath, PROFILE_DIR};

Expand Down Expand Up @@ -41,6 +42,23 @@ struct ActivationArgs {
ephemeral: bool,
}

#[derive(clap::Args, Debug)]
struct TimeoutArgs {
/// Set the timeout for the action, in seconds. 0 disables the timeout and
/// waits indefinitely for the action to finish
#[arg(short, long, default_value_t = 30u64)]
timeout: u64,
}

impl From<&TimeoutArgs> for Option<Duration> {
fn from(timeout_args: &TimeoutArgs) -> Self {
match timeout_args.timeout {
0 => None,
secs => Some(Duration::from_secs(secs)),
}
}
}

#[derive(clap::Args, Debug)]
struct StorePathArg {
/// The store path containing the system-manager profile
Expand All @@ -64,11 +82,15 @@ enum Action {
store_path_arg: StorePathArg,
#[command(flatten)]
activation_args: ActivationArgs,
#[command(flatten)]
timeout_args: TimeoutArgs,
},
/// Deactivate the system-manager profile (remove managed configuration)
Deactivate {
#[command(flatten)]
store_path_arg: OptionalStorePathArg,
#[command(flatten)]
timeout_args: TimeoutArgs,
},
/// Pre-populate files without starting services
Prepopulate {
Expand Down Expand Up @@ -104,17 +126,23 @@ fn go(args: Args) -> Result<()> {
Action::Activate {
store_path_arg: StorePathArg { store_path },
activation_args: ActivationArgs { ephemeral },
} => system_manager_engine::activate::activate(&store_path, ephemeral),
timeout_args,
} => system_manager_engine::activate::activate(
&store_path,
ephemeral,
&Option::<Duration>::from(&timeout_args),
),

Action::Deactivate {
store_path_arg: OptionalStorePathArg { store_path },
timeout_args,
} => {
// Log which store path we're using if it was auto-detected
if store_path.is_none() {
let path = std::path::Path::new(PROFILE_DIR).join("system-manager");
log::info!("No store path provided, using {}", path.display());
}
system_manager_engine::deactivate::deactivate()
system_manager_engine::deactivate::deactivate(&Option::<Duration>::from(&timeout_args))
}

Action::Prepopulate {
Expand Down
Loading
Loading