You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
3.0 KiB
107 lines
3.0 KiB
use crate::async_utils::sleep;
|
|
use crate::command_runner::{CommandRunner, SetuidCommandRunner};
|
|
use crate::symbols::Symbol;
|
|
use async_trait::async_trait;
|
|
use std::error::Error;
|
|
use std::ffi::OsStr;
|
|
use std::path::Path;
|
|
use std::time::Duration;
|
|
|
|
#[derive(Debug)]
|
|
pub struct UserService<'a, S: AsRef<Path>, U: AsRef<str>> {
|
|
socket_path: S,
|
|
service_name: &'a str,
|
|
command_runner: SetuidCommandRunner<U>,
|
|
}
|
|
|
|
impl<S: AsRef<Path>, U: AsRef<str>> UserService<'static, S, U> {
|
|
pub const fn new(socket_path: S, user_name: U, service_name: &'static str) -> Self {
|
|
Self {
|
|
socket_path,
|
|
service_name,
|
|
command_runner: SetuidCommandRunner::new(user_name),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: AsRef<Path>, U: AsRef<str>> UserService<'_, S, U> {
|
|
async fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
|
|
let mut tries = 5;
|
|
loop {
|
|
let result = self.command_runner.run_with_args("systemctl", args).await?;
|
|
if result.status.success() {
|
|
return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
|
|
}
|
|
let raw_stderr = String::from_utf8(result.stderr)?;
|
|
let stderr = raw_stderr.trim_end();
|
|
if stderr != "Failed to connect to bus: No such file or directory" {
|
|
return Err(stderr.into());
|
|
}
|
|
tries -= 1;
|
|
if tries == 0 {
|
|
return Err("Gave up waiting for dbus to appear".to_string().into());
|
|
}
|
|
sleep(Duration::from_millis(500)).await;
|
|
}
|
|
}
|
|
|
|
async fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
|
loop {
|
|
let active_state = self
|
|
.systemctl_wait_for_dbus(args![
|
|
"--user",
|
|
"show",
|
|
"--property",
|
|
"ActiveState",
|
|
self.service_name,
|
|
])
|
|
.await?;
|
|
match active_state.as_ref() {
|
|
"ActiveState=activating" => sleep(Duration::from_millis(500)).await,
|
|
"ActiveState=active" => return Ok(true),
|
|
"ActiveState=failed" => {
|
|
return Err(
|
|
String::from_utf8(
|
|
self
|
|
.command_runner
|
|
.get_output(
|
|
"journalctl",
|
|
args!["--user", format!("--user-unit={}", self.service_name)],
|
|
)
|
|
.await?,
|
|
)?
|
|
.into(),
|
|
)
|
|
}
|
|
_ => return Ok(false),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl<S: AsRef<Path>, U: AsRef<str>> Symbol for UserService<'_, S, U> {
|
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
|
self.check_if_service().await
|
|
}
|
|
|
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
|
self
|
|
.systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])
|
|
.await?;
|
|
self
|
|
.systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])
|
|
.await?;
|
|
|
|
loop {
|
|
if !(self.check_if_service().await?) {
|
|
return Err("Generic error".into());
|
|
}
|
|
|
|
if self.socket_path.as_ref().exists() {
|
|
return Ok(());
|
|
}
|
|
sleep(Duration::from_millis(500)).await;
|
|
}
|
|
}
|
|
}
|