Skip to content

Commit

Permalink
Merge pull request #30 from rgwood/reload-units
Browse files Browse the repository at this point in the history
Reload units
  • Loading branch information
rgwood authored Jan 15, 2025
2 parents c1ec910 + baa91c6 commit 82f2de2
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 12 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ jobs:
matrix:
include:
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
os: ubuntu-24.04
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
os: ubuntu-24.04
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "systemctl-tui"
description = "A simple TUI for interacting with systemd services and their logs"
homepage = "https://github.com/rgwood/systemctl-tui"
repository = "https://github.com/rgwood/systemctl-tui"
version = "0.3.9"
version = "0.3.10"
edition = "2021"
authors = ["Reilly Wood"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ pub enum Action {
ScrollDown(u16),
ScrollToTop,
ScrollToBottom,
EditUnitFile { path: String },
EditUnitFile { unit: UnitId, path: String },
Noop,
}
22 changes: 20 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{process::Command, sync::Arc};

use anyhow::{Context, Result};
use log::error;
use tokio::sync::{mpsc, Mutex};
use tracing::debug;

Expand Down Expand Up @@ -93,16 +94,33 @@ impl App {
Action::Suspend => self.should_suspend = true,
Action::Resume => self.should_suspend = false,
Action::Resize(_, _) => terminal.render().await,
Action::EditUnitFile { path } => {
// This would normally be in home.rs, but it needs to do some terminal and event handling stuff that's easier here
Action::EditUnitFile { unit, path } => {
event.stop();
let mut tui = terminal.tui.lock().await;
tui.exit()?;

let read_unit_file_contents = || match std::fs::read_to_string(&path) {
Ok(contents) => contents,
Err(e) => {
error!("Failed to read unit file `{}`: {}", path, e);
"".to_string()
},
};

let unit_file_contents = read_unit_file_contents();
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
match Command::new(&editor).arg(path).status() {
match Command::new(&editor).arg(&path).status() {
Ok(_) => {
tui.enter()?;
tui.clear()?;
event = EventHandler::new(self.home.clone(), action_tx.clone());

let new_unit_file_contents = read_unit_file_contents();
if unit_file_contents != new_unit_file_contents {
action_tx.send(Action::ReloadService(unit))?;
}

action_tx.send(Action::EnterMode(Mode::ServiceList))?;
},
Err(e) => {
Expand Down
14 changes: 12 additions & 2 deletions src/components/home.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ impl Home {
self.service_action(service, "Stop".into(), cancel_token, future);
}

fn reload_service(&mut self, service: UnitId) {
let cancel_token = CancellationToken::new();
let future = systemd::reload(service.scope, cancel_token.clone());
self.service_action(service, "Reload".into(), cancel_token, future);
}

fn restart_service(&mut self, service: UnitId) {
let cancel_token = CancellationToken::new();
let future = systemd::restart_service(service.clone(), cancel_token.clone());
Expand Down Expand Up @@ -564,15 +570,18 @@ impl Component for Home {
MenuItem::new("Start", Action::StartService(selected.id())),
MenuItem::new("Stop", Action::StopService(selected.id())),
MenuItem::new("Restart", Action::RestartService(selected.id())),
MenuItem::new("Reload", Action::ReloadService(selected.id())),
// TODO add these
// MenuItem::new("Reload", Action::ReloadService(selected.clone())),
// MenuItem::new("Enable", Action::EnableService(selected.clone())),
// MenuItem::new("Disable", Action::DisableService(selected.clone())),
];

if let Some(Ok(file_path)) = &selected.file_path {
menu_items.push(MenuItem::new("Copy unit file path to clipboard", Action::CopyUnitFilePath));
menu_items.push(MenuItem::new("Edit unit file", Action::EditUnitFile { path: file_path.clone() }));
menu_items.push(MenuItem::new(
"Edit unit file",
Action::EditUnitFile { unit: selected.id(), path: file_path.clone() },
));
}

self.menu_items = StatefulList::with_items(menu_items);
Expand Down Expand Up @@ -652,6 +661,7 @@ impl Component for Home {

Action::StartService(service_name) => self.start_service(service_name),
Action::StopService(service_name) => self.stop_service(service_name),
Action::ReloadService(service_name) => self.reload_service(service_name),
Action::RestartService(service_name) => self.restart_service(service_name),
Action::RefreshServices => {
let tx = self.action_tx.clone().unwrap();
Expand Down
31 changes: 28 additions & 3 deletions src/systemd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::str;
use std::process::Command;

use anyhow::{bail, Result};
use anyhow::{bail, Context, Result};
use log::error;
use tokio_util::sync::CancellationToken;
use tracing::info;
Expand Down Expand Up @@ -198,7 +198,6 @@ pub async fn start_service(service: UnitId, cancel_token: CancellationToken) ->
// god these select macros are ugly, is there really no better way to select?
tokio::select! {
_ = cancel_token.cancelled() => {
// The token was cancelled
anyhow::bail!("cancelled");
}
result = start_service(service) => {
Expand All @@ -218,7 +217,6 @@ pub async fn stop_service(service: UnitId, cancel_token: CancellationToken) -> R
// god these select macros are ugly, is there really no better way to select?
tokio::select! {
_ = cancel_token.cancelled() => {
// The token was cancelled
anyhow::bail!("cancelled");
}
result = stop_service(service) => {
Expand All @@ -227,6 +225,29 @@ pub async fn stop_service(service: UnitId, cancel_token: CancellationToken) -> R
}
}

pub async fn reload(scope: UnitScope, cancel_token: CancellationToken) -> Result<()> {
async fn reload_(scope: UnitScope) -> Result<()> {
let connection = get_connection(scope).await?;
let manager_proxy: ManagerProxy<'_> = ManagerProxy::new(&connection).await?;
let error_message = match scope {
UnitScope::Global => "Failed to reload units, probably because superuser permissions are needed. Try running `sudo systemctl daemon-reload`",
UnitScope::User => "Failed to reload units. Try running `systemctl --user daemon-reload`",
};
manager_proxy.reload().await.context(error_message)?;
Ok(())
}

// god these select macros are ugly, is there really no better way to select?
tokio::select! {
_ = cancel_token.cancelled() => {
anyhow::bail!("cancelled");
}
result = reload_(scope) => {
result
}
}
}

async fn get_connection(scope: UnitScope) -> Result<Connection, anyhow::Error> {
match scope {
UnitScope::Global => Ok(Connection::system().await?),
Expand Down Expand Up @@ -285,6 +306,10 @@ pub trait Manager {
#[dbus_proxy(name = "StopUnit")]
fn stop_unit(&self, name: String, mode: String) -> zbus::Result<zvariant::OwnedObjectPath>;

/// [📖](https://www.freedesktop.org/software/systemd/man/systemd.directives.html#ReloadUnit()) Call interface method `ReloadUnit`.
#[dbus_proxy(name = "ReloadUnit")]
fn reload_unit(&self, name: String, mode: String) -> zbus::Result<zvariant::OwnedObjectPath>;

/// [📖](https://www.freedesktop.org/software/systemd/man/systemd.directives.html#RestartUnit()) Call interface method `RestartUnit`.
#[dbus_proxy(name = "RestartUnit")]
fn restart_unit(&self, name: String, mode: String) -> zbus::Result<zvariant::OwnedObjectPath>;
Expand Down

0 comments on commit 82f2de2

Please sign in to comment.