summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Neumann <mneumann@ntecs.de>2021-07-13 10:05:47 +0200
committerGitHub <noreply@github.com>2021-07-13 10:05:47 +0200
commit33ace1c0cc88cf54bced5e04a229c690a3ffe08b (patch)
tree4d40823ed4a957d5dcec7b156ab6bbaf367e6fd9
parente9a75e9e2198ea54327cb5cb9363159efede6933 (diff)
Add system service manager abstraction (#338)
* Refactor code to use abstract `SystemServiceManager`. * Implement Debug for UserManager This simplifies code that embeds a `UserManager` and has to implement `Debug` on it's own as `Debug` cannot be automatically derived. * Add `SystemdServiceManager` * Add NullSystemServiceManager that always fail This can be used as fallback manager where commands that don't need a SystemServiceManager would still work. * Bring in support for OpenRC / BSD service(8) * Add feature flag "openrc" to override the default system service manager that is selected based on the target operation system. For instance, Gentoo has target_os = "linux", but uses OpenRC. OpenRC can also be used on other systems. * Code roughly based on PR #196.
-rw-r--r--Cargo.lock2
-rw-r--r--common/tedge_users/src/unix.rs7
-rw-r--r--common/tedge_users/src/windows.rs7
-rw-r--r--tedge/Cargo.toml1
-rw-r--r--tedge/src/cli/connect/cli.rs2
-rw-r--r--tedge/src/cli/connect/command.rs83
-rw-r--r--tedge/src/cli/connect/error.rs2
-rw-r--r--tedge/src/cli/disconnect/cli.rs2
-rw-r--r--tedge/src/cli/disconnect/disconnect_bridge.rs95
-rw-r--r--tedge/src/cli/disconnect/error.rs3
-rw-r--r--tedge/src/command.rs3
-rw-r--r--tedge/src/main.rs18
-rw-r--r--tedge/src/services/mod.rs240
-rw-r--r--tedge/src/services/mosquitto.rs7
-rw-r--r--tedge/src/services/tedge_mapper_az.rs7
-rw-r--r--tedge/src/services/tedge_mapper_c8y.rs7
-rw-r--r--tedge/src/system_services/command_builder.rs29
-rw-r--r--tedge/src/system_services/error.rs71
-rw-r--r--tedge/src/system_services/manager.rs40
-rw-r--r--tedge/src/system_services/manager_ext.rs62
-rw-r--r--tedge/src/system_services/managers/bsd.rs185
-rw-r--r--tedge/src/system_services/managers/mod.rs6
-rw-r--r--tedge/src/system_services/managers/null.rs35
-rw-r--r--tedge/src/system_services/managers/openrc.rs183
-rw-r--r--tedge/src/system_services/managers/systemd.rs222
-rw-r--r--tedge/src/system_services/mod.rs20
-rw-r--r--tedge/src/system_services/services.rs21
27 files changed, 967 insertions, 393 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1ed8f560..e3b8397a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "addr2line"
version = "0.14.1"
diff --git a/common/tedge_users/src/unix.rs b/common/tedge_users/src/unix.rs
index aef224cf..a37e8784 100644
--- a/common/tedge_users/src/unix.rs
+++ b/common/tedge_users/src/unix.rs
@@ -1,3 +1,4 @@
+use std::fmt;
use std::rc::Rc;
use std::sync::Mutex;
@@ -21,6 +22,12 @@ pub struct UserManager {
inner: Rc<Mutex<InnerUserManager>>,
}
+impl fmt::Debug for UserManager {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UserManager").finish()
+ }
+}
+
struct InnerUserManager {
users: Vec<String>,
guard: Option<users::switch::SwitchUserGuard>,
diff --git a/common/tedge_users/src/windows.rs b/common/tedge_users/src/windows.rs
index eedb2745..f529cdd2 100644
--- a/common/tedge_users/src/windows.rs
+++ b/common/tedge_users/src/windows.rs
@@ -1,3 +1,4 @@
+use std::fmt;
use std::marker::PhantomData;
use std::rc::Rc;
@@ -6,6 +7,12 @@ pub struct UserManager {
_force_not_send: PhantomData<Rc<()>>,
}
+impl fmt::Debug for UserManager {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UserManager").finish()
+ }
+}
+
pub struct UserGuard {
_force_not_send: PhantomData<Rc<()>>,
}
diff --git a/tedge/Cargo.toml b/tedge/Cargo.toml
index 412c14f4..2326275a 100644
--- a/tedge/Cargo.toml
+++ b/tedge/Cargo.toml
@@ -46,3 +46,4 @@ integration-test = []
mosquitto-available = [] # Enable tests requesting mosquitto installed
root-access = [] # Enable tests requesting root access
tedge-user = [] # Enable tests requesting a tedge user
+openrc = [] # Enable usage of OpenRC
diff --git a/tedge/src/cli/connect/cli.rs b/tedge/src/cli/connect/cli.rs
index ed2539ae..42e68885 100644
--- a/tedge/src/cli/connect/cli.rs
+++ b/tedge/src/cli/connect/cli.rs
@@ -32,6 +32,7 @@ impl BuildCommand for TEdgeConnectOpt {
cloud: Cloud::C8y,
common_mosquitto_config: CommonMosquittoConfig::default(),
is_test_connection,
+ service_manager: context.service_manager.clone(),
},
TEdgeConnectOpt::Az { is_test_connection } => ConnectCommand {
config_location: context.config_location,
@@ -39,6 +40,7 @@ impl BuildCommand for TEdgeConnectOpt {
cloud: Cloud::Azure,
common_mosquitto_config: CommonMosquittoConfig::default(),
is_test_connection,
+ service_manager: context.service_manager.clone(),
},
}
.into_boxed())
diff --git a/tedge/src/cli/connect/command.rs b/tedge/src/cli/connect/command.rs
index ca4dcc3d..a4452380 100644
--- a/tedge/src/cli/connect/command.rs
+++ b/tedge/src/cli/connect/command.rs
@@ -1,18 +1,15 @@
use crate::{
cli::connect::*,
command::{Command, ExecutionContext},
- services::{
- self, mosquitto::MosquittoService, tedge_mapper_az::TedgeMapperAzService,
- tedge_mapper_c8y::TedgeMapperC8yService, SystemdService,
- },
+ system_services::*,
utils::paths,
ConfigError,
};
use mqtt_client::{Client, Message, MqttClient, Topic, TopicFilter};
use std::path::{Path, PathBuf};
+use std::sync::Arc;
use std::time::Duration;
use tedge_config::*;
-use tedge_users::{UserManager, ROOT_USER};
use tempfile::NamedTempFile;
use tokio::time::timeout;
use which::which;
@@ -31,6 +28,7 @@ pub struct ConnectCommand {
pub cloud: Cloud,
pub common_mosquitto_config: CommonMosquittoConfig,
pub is_test_connection: bool,
+ pub service_manager: Arc<dyn SystemServiceManager>,
}
pub enum Cloud {
@@ -39,6 +37,15 @@ pub enum Cloud {
}
impl Cloud {
+ fn dependent_mapper_service(&self) -> SystemService {
+ match self {
+ Cloud::Azure => SystemService::TEdgeMapperAz,
+ Cloud::C8y => SystemService::TEdgeMapperC8y,
+ }
+ }
+}
+
+impl Cloud {
fn as_str(&self) -> &'static str {
match self {
Self::Azure => "Azure",
@@ -56,7 +63,7 @@ impl Command for ConnectCommand {
}
}
- fn execute(&self, context: &ExecutionContext) -> Result<(), anyhow::Error> {
+ fn execute(&self, _context: &ExecutionContext) -> Result<(), anyhow::Error> {
let mut config = self.config_repository.load()?;
if self.is_test_connection {
@@ -82,7 +89,7 @@ impl Command for ConnectCommand {
&bridge_config,
&self.cloud,
&updated_mosquitto_config,
- &context.user_manager,
+ self.service_manager.as_ref(),
&self.config_location,
)?;
@@ -282,11 +289,11 @@ fn new_bridge(
bridge_config: &BridgeConfig,
cloud: &Cloud,
common_mosquitto_config: &CommonMosquittoConfig,
- user_manager: &UserManager,
+ service_manager: &dyn SystemServiceManager,
config_location: &TEdgeConfigLocation,
) -> Result<(), ConnectError> {
- println!("Checking if systemd is available.\n");
- let () = services::systemd_available()?;
+ println!("Checking if {} is available.\n", service_manager.name());
+ let () = service_manager.check_operational()?;
println!("Checking if configuration for requested bridge already exists.\n");
let () = bridge_config_exists(config_location, bridge_config)?;
@@ -304,7 +311,7 @@ fn new_bridge(
}
println!("Restarting mosquitto service.\n");
- if let Err(err) = MosquittoService.restart(user_manager) {
+ if let Err(err) = service_manager.restart_service(SystemService::Mosquitto) {
clean_up(config_location, bridge_config)?;
return Err(err.into());
}
@@ -318,7 +325,7 @@ fn new_bridge(
));
println!("Persisting mosquitto on reboot.\n");
- if let Err(err) = MosquittoService.enable(user_manager) {
+ if let Err(err) = service_manager.enable_service(SystemService::Mosquitto) {
clean_up(config_location, bridge_config)?;
return Err(err.into());
}
@@ -331,14 +338,8 @@ fn new_bridge(
if which("tedge_mapper").is_err() {
println!("Warning: tedge_mapper is not installed. We recommend to install it.\n");
} else {
- match cloud {
- Cloud::Azure => {
- start_and_enable_tedge_mapper_az(user_manager);
- }
- Cloud::C8y => {
- start_and_enable_tedge_mapper_c8y(user_manager);
- }
- }
+ service_manager
+ .start_and_enable_service(cloud.dependent_mapper_service(), std::io::stdout());
}
}
@@ -395,48 +396,6 @@ fn write_bridge_config_to_file(
Ok(())
}
-fn start_and_enable_tedge_mapper_c8y(user_manager: &UserManager) {
- let _root_guard = user_manager.become_user(ROOT_USER);
- let mut failed = false;
-
- println!("Starting tedge-mapper service.\n");
- if let Err(err) = TedgeMapperC8yService.restart(user_manager) {
- println!("Failed to stop tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- println!("Persisting tedge-mapper on reboot.\n");
- if let Err(err) = TedgeMapperC8yService.enable(user_manager) {
- println!("Failed to enable tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- if !failed {
- println!("tedge-mapper service successfully started and enabled!\n");
- }
-}
-
-fn start_and_enable_tedge_mapper_az(user_manager: &UserManager) {
- let _root_guard = user_manager.become_user(ROOT_USER);
- let mut failed = false;
-
- println!("Starting tedge-mapper service.\n");
- if let Err(err) = TedgeMapperAzService.restart(user_manager) {
- println!("Failed to stop tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- println!("Persisting tedge-mapper on reboot.\n");
- if let Err(err) = TedgeMapperAzService.enable(user_manager) {
- println!("Failed to enable tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- if !failed {
- println!("tedge-mapper service successfully started and enabled!\n");
- }
-}
-
fn get_bridge_config_file_path(
config_location: &TEdgeConfigLocation,
bridge_config: &BridgeConfig,
diff --git a/tedge/src/cli/connect/error.rs b/tedge/src/cli/connect/error.rs
index c64553b4..426c628c 100644
--- a/tedge/src/cli/connect/error.rs
+++ b/tedge/src/cli/connect/error.rs
@@ -25,7 +25,7 @@ pub enum ConnectError {
UrlParse(#[from] url::ParseError),
#[error(transparent)]
- ServicesError(#[from] crate::services::ServicesError),
+ SystemServiceError(#[from] crate::system_services::SystemServiceError),
#[error("Operation timed out. Is mosquitto running?")]
TimeoutElapsedError,
diff --git a/tedge/src/cli/disconnect/cli.rs b/tedge/src/cli/disconnect/cli.rs
index ad8721fa..d58508e4 100644
--- a/tedge/src/cli/disconnect/cli.rs
+++ b/tedge/src/cli/disconnect/cli.rs
@@ -21,12 +21,14 @@ impl BuildCommand for TEdgeDisconnectBridgeCli {
config_file: C8Y_CONFIG_FILENAME.into(),
cloud: Cloud::C8y,
use_mapper: true,
+ service_manager: context.service_manager.clone(),
},
TEdgeDisconnectBridgeCli::Az => DisconnectBridgeCommand {
config_location: context.config_location,
config_file: AZURE_CONFIG_FILENAME.into(),
cloud: Cloud::Azure,
use_mapper: true,
+ service_manager: context.service_manager.clone(),
},
};
Ok(cmd.into_boxed())
diff --git a/tedge/src/cli/disconnect/disconnect_bridge.rs b/tedge/src/cli/disconnect/disconnect_bridge.rs
index 1730682b..4f004323 100644
--- a/tedge/src/cli/disconnect/disconnect_bridge.rs
+++ b/tedge/src/cli/disconnect/disconnect_bridge.rs
@@ -1,21 +1,27 @@
use crate::cli::disconnect::error::*;
use crate::command::*;
-use crate::services::{
- mosquitto::MosquittoService, tedge_mapper_az::TedgeMapperAzService,
- tedge_mapper_c8y::TedgeMapperC8yService, SystemdService,
-};
+use crate::system_services::*;
+use std::sync::Arc;
use tedge_config::TEdgeConfigLocation;
-use tedge_users::*;
use which::which;
const TEDGE_BRIDGE_CONF_DIR_PATH: &str = "mosquitto-conf";
-#[derive(Debug)]
+#[derive(Copy, Clone, Debug)]
pub enum Cloud {
C8y,
Azure,
}
+impl Cloud {
+ fn dependent_mapper_service(&self) -> SystemService {
+ match self {
+ Cloud::Azure => SystemService::TEdgeMapperAz,
+ Cloud::C8y => SystemService::TEdgeMapperC8y,
+ }
+ }
+}
+
impl From<Cloud> for String {
fn from(val: Cloud) -> Self {
match val {
@@ -31,6 +37,7 @@ pub struct DisconnectBridgeCommand {
pub config_file: String,
pub cloud: Cloud,
pub use_mapper: bool,
+ pub service_manager: Arc<dyn SystemServiceManager>,
}
impl Command for DisconnectBridgeCommand {
@@ -38,8 +45,8 @@ impl Command for DisconnectBridgeCommand {
format!("remove the bridge to disconnect {:?} cloud", self.cloud)
}
- fn execute(&self, context: &ExecutionContext) -> Result<(), anyhow::Error> {
- match self.stop_bridge(&context.user_manager) {
+ fn execute(&self, _context: &ExecutionContext) -> Result<(), anyhow::Error> {
+ match self.stop_bridge() {
Ok(()) | Err(DisconnectBridgeError::BridgeFileDoesNotExist) => Ok(()),
Err(err) => Err(err.into()),
}
@@ -47,23 +54,21 @@ impl Command for DisconnectBridgeCommand {
}
impl DisconnectBridgeCommand {
- fn stop_bridge(&self, user_manager: &UserManager) -> Result<(), DisconnectBridgeError> {
+ fn service_manager(&self) -> &dyn SystemServiceManager {
+ self.service_manager.as_ref()
+ }
+
+ fn stop_bridge(&self) -> Result<(), DisconnectBridgeError> {
// If this fails, do not continue with applying changes and stopping/disabling tedge-mapper.
self.remove_bridge_config_file()?;
// Ignore failure
- let _ = self.apply_changes_to_mosquitto(user_manager);
+ let _ = self.apply_changes_to_mosquitto();
// Only C8Y changes the status of tedge-mapper
if self.use_mapper && which("tedge_mapper").is_ok() {
- match self.cloud {
- Cloud::Azure => {
- self.stop_and_disable_tedge_mapper_az(user_manager);
- }
- Cloud::C8y => {
- self.stop_and_disable_tedge_mapper_c8y(user_manager);
- }
- }
+ self.service_manager()
+ .stop_and_disable_service(self.cloud.dependent_mapper_service(), std::io::stdout());
}
Ok(())
@@ -99,57 +104,15 @@ impl DisconnectBridgeCommand {
// Deviation from specification:
// Check if mosquitto is running, restart only if it was active before, if not don't do anything.
- fn apply_changes_to_mosquitto(
- &self,
- user_manager: &UserManager,
- ) -> Result<(), DisconnectBridgeError> {
+ fn apply_changes_to_mosquitto(&self) -> Result<(), DisconnectBridgeError> {
println!("Applying changes to mosquitto.\n");
- if MosquittoService.is_active()? {
- MosquittoService.restart(user_manager)?;
+
+ if self
+ .service_manager()
+ .restart_service_if_running(SystemService::Mosquitto)?
+ {
println!("{:?} Bridge successfully disconnected!\n", self.cloud);
}
Ok(())
}
-
- fn stop_and_disable_tedge_mapper_c8y(&self, user_manager: &UserManager) {
- let _root_guard = user_manager.become_user(ROOT_USER);
- let mut failed = false;
-
- println!("Stopping tedge-mapper service.\n");
- if let Err(err) = TedgeMapperC8yService.stop(user_manager) {
- println!("Failed to stop tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- println!("Disabling tedge-mapper service.\n");
- if let Err(err) = TedgeMapperC8yService.disable(user_manager) {
- println!("Failed to disable tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- if !failed {
- println!("tedge-mapper service successfully stopped and disabled!\n");
- }
- }
-
- fn stop_and_disable_tedge_mapper_az(&self, user_manager: &UserManager) {
- let _root_guard = user_manager.become_user(ROOT_USER);
- let mut failed = false;
-
- println!("Stopping tedge-mapper service.\n");
- if let Err(err) = TedgeMapperAzService.stop(user_manager) {
- println!("Failed to stop tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- println!("Disabling tedge-mapper service.\n");
- if let Err(err) = TedgeMapperAzService.disable(user_manager) {
- println!("Failed to disable tedge-mapper service: {:?}", err);
- failed = true;
- }
-
- if !failed {
- println!("tedge-mapper service successfully stopped and disabled!\n");
- }
- }
}
diff --git a/tedge/src/cli/disconnect/error.rs b/tedge/src/cli/disconnect/error.rs
index b24109bc..fd38bf0d 100644
--- a/tedge/src/cli/disconnect/error.rs
+++ b/tedge/src/cli/disconnect/error.rs
@@ -1,4 +1,3 @@
-use crate::services;
use std::path::PathBuf;
#[derive(thiserror::Error, Debug)]
@@ -13,7 +12,7 @@ pub enum DisconnectBridgeError {
IoError(#[from] std::io::Error),
#[error(transparent)]
- ServicesError(#[from] services::ServicesError),
+ SystemServiceError(#[from] crate::system_services::SystemServiceError),
#[error("Bridge file does not exist.")]
BridgeFileDoesNotExist,
diff --git a/tedge/src/command.rs b/tedge/src/command.rs
index 1c9cb3f2..3879c0b8 100644
--- a/tedge/src/command.rs
+++ b/tedge/src/command.rs
@@ -1,3 +1,5 @@
+use crate::system_services::*;
+use std::sync::Arc;
use tedge_users::UserManager;
/// A trait to be implemented by all tedge sub-commands.
@@ -162,6 +164,7 @@ pub trait BuildCommand {
pub struct BuildContext {
pub config_repository: tedge_config::TEdgeConfigRepository,
pub config_location: tedge_config::TEdgeConfigLocation,
+ pub service_manager: Arc<dyn SystemServiceManager>,
}
/// The execution context of a command.
diff --git a/tedge/src/main.rs b/tedge/src/main.rs
index 2e429a86..bb467f84 100644
--- a/tedge/src/main.rs
+++ b/tedge/src/main.rs
@@ -1,13 +1,16 @@
#![forbid(unsafe_code)]
#![deny(clippy::mem_forget)]
+use crate::system_services::*;
use anyhow::Context;
+use std::sync::Arc;
use structopt::StructOpt;
+use tedge_users::UserManager;
mod cli;
mod command;
mod error;
-mod services;
+mod system_services;
mod utils;
type ConfigError = crate::error::TEdgeError;
@@ -34,6 +37,7 @@ fn main() -> anyhow::Result<()> {
let build_context = BuildContext {
config_repository,
config_location: tedge_config_location,
+ service_manager: service_manager(context.user_manager.clone()),
};
let cmd = opt
@@ -44,3 +48,15 @@ fn main() -> anyhow::Result<()> {
cmd.execute(&context)
.with_context(|| format!("failed to {}", cmd.description()))
}
+
+fn service_manager(user_manager: UserManager) -> Arc<dyn SystemServiceManager> {
+ if cfg!(feature = "openrc") {
+ Arc::new(OpenRcServiceManager::new(user_manager))
+ } else if cfg!(target_os = "linux") {
+ Arc::new(SystemdServiceManager::new(user_manager))
+ } else if cfg!(target_os = "freebsd") {
+ Arc::new(BsdServiceManager::new(user_manager))
+ } else {
+ Arc::new(NullSystemServiceManager)
+ }
+}
diff --git a/tedge/src/services/mod.rs b/tedge/src/services/mod.rs
deleted file mode 100644
index 3abed534..00000000
--- a/tedge/src/services/mod.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use crate::services::SystemdError::{
- ServiceNotFound, ServiceNotLoaded, SystemdNotAvailable, UnhandledReturnCode, UnspecificError,
-};
-use crate::utils::paths;
-use std::process::ExitStatus;
-use tedge_users::*;
-
-pub mod mosquitto;
-pub mod tedge_mapper_az;
-pub mod tedge_mapper_c8y;
-
-type ExitCode = i32;
-
-const SYSTEMCTL_OK: ExitCode = 0;
-const SYSTEMCTL_ERROR_GENERIC: ExitCode = 1;
-const SYSTEMCTL_ERROR_UNIT_IS_NOT_ACTIVE: ExitCode = 3;
-const SYSTEMCTL_ERROR_SERVICE_NOT_FOUND: ExitCode = 5;
-const SYSTEMCTL_ERROR_SERVICE_NOT_LOADED: ExitCode = 5;
-
-const SYSTEMCTL_BIN: &str = "systemctl";
-
-pub trait SystemdService {
- const SERVICE_NAME: &'static str;
-
- fn stop(&self, user_manager: &UserManager) -> Result<(), ServicesError> {
- match call_systemd_subcmd_sudo(SystemCtlCmd::Stop, Self::SERVICE_NAME, user_manager)? {
- SYSTEMCTL_OK => Ok(()),
- SYSTEMCTL_ERROR_GENERIC => Err(ServicesError::SystemdError(UnspecificError {
- service: Self::SERVICE_NAME,
- cmd: SystemCtlCmd::Stop.as_str(),
- hint: "Lacking permissions.",
- })),
- SYSTEMCTL_ERROR_SERVICE_NOT_LOADED => {
- Err(ServicesError::SystemdError(ServiceNotLoaded {
- service: Self::SERVICE_NAME,
- }))
- }
- code => Err(ServicesError::SystemdError(UnhandledReturnCode { code })),
- }
- }
-
- // Note that restarting a unit with this command does not necessarily flush out all of the unit's resources before it is started again.
- // For example, the per-service file descriptor storage facility (see FileDescriptorStoreMax= in systemd.service(5)) will remain intact
- // as long as the unit has a job pending, and is only cleared when the unit is fully stopped and no jobs are pending anymore.
- // If it is intended that the file descriptor store is flushed out, too, during a restart operation an explicit
- // systemctl stop command followed by systemctl start should be issued.
- fn restart(&self, user_manager: &UserManager) -> Result<(), ServicesError> {
- match call_systemd_subcmd_sudo(SystemCtlCmd::Restart, Self::SERVICE_NAME, user_manager)? {
- SYSTEMCTL_OK => Ok(()),
- SYSTEMCTL_ERROR_GENERIC => Err(ServicesError::SystemdError(UnspecificError {
- service: Self::SERVICE_NAME,
- cmd: SystemCtlCmd::Restart.as_str(),
- hint: "Lacking permissions or service's process exited with error code.",
- })),
- SYSTEMCTL_ERROR_SERVICE_NOT_FOUND => {
- Err(ServicesError::SystemdError(ServiceNotFound {
- service: Self::SERVICE_NAME,
- }))
- }
- code => Err(ServicesError::SystemdError(UnhandledReturnCode { code })),
- }
- }
-
- fn enable(&self, user_manager: &UserManager) -> Result<(), ServicesError> {
- match call_systemd_subcmd_sudo(SystemCtlCmd::Enable, Self::SERVICE_NAME, user_manager)? {
- SYSTEMCTL_OK => Ok(()),
- SYSTEMCTL_ERROR_GENERIC => Err(ServicesError::SystemdError(UnspecificError {
- service: Self::SERVICE_NAME,
- cmd: SystemCtlCmd::Enable.as_str(),
- hint: "Lacking permissions.",
- })),
- code => Err(ServicesError::SystemdError(UnhandledReturnCode { code })),
- }
- }
-
- fn disable(&self, user_manager: &UserManager) -> Result<(), ServicesError> {
- match call_systemd_subcmd_sudo(SystemCtlCmd::Disable, Self::SERVICE_NAME, user_manager)? {
- SYSTEMCTL_OK => Ok(()),
- SYSTEMCTL_ERROR_GENERIC => Err(ServicesError::SystemdError(UnspecificError {
- service: Self::SERVICE_NAME,
- cmd: SystemCtlCmd::Disable.as_str(),
- hint: "Lacking permissions.",
- })),
- code => Err(ServicesError::SystemdError(UnhandledReturnCode { code })),
- }
- }
-
- fn is_active(&self) -> Result<bool, ServicesError> {
- match call_systemd_subcmd(SystemCtlCmd::IsActive, Self::SERVICE_NAME)? {
- SYSTEMCTL_OK => Ok(true),
- SYSTEMCTL_ERROR_UNIT_IS_NOT_ACTIVE => Ok(false),
- code => Err(ServicesError::SystemdError(UnhandledReturnCode { code })),
- }
- }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum SystemdError {
- #[error("Systemd returned unspecific error for service {service} while performing {cmd} it.\nHint: {hint}")]
- UnspecificError {
- service: &'static str,
- cmd: &'static str,
- hint: &'static str,
- },
-
- #[error("Service {service} not found. Install {service} to use this command.")]
- ServiceNotFound { service: &'static str },
-
- #[error("Service {service} not loaded.")]
- ServiceNotLoaded { service: &'static str },
-
- #[error(
- "Systemd is not available on the system or elevated permissions have not been granted."
- )]
- SystemdNotAvailable,
-
- #[error("Returned exit code: '{code:?}' for: systemd' is unhandled.")]
- UnhandledReturnCode { code: i32 },
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum ServicesError {
- #[error(transparent)]
- IoError(#[from] std::io::Error),
-
- #[error(transparent)]
- SystemdError(#[from] SystemdError),
-
- #[error(transparent)]
- PathsError(#[from] paths::PathsError),
-
- #[error("Unexpected value for exit status.")]
- UnexpectedExitStatus,
-}
-
-fn cmd_nullstdio_args_with_code(command: &str, args: &[&str]) -> Result<ExitStatus, ServicesError> {
- Ok(std::process::Command::new(command)
- .args(args)
- .stdout(std::process::Stdio::null())
- .stderr(std::process::Stdio::null())
- .status()?)