A library for writing host-specific, single-binary configuration management and deployment tools
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.

113 lines
3.2 KiB

7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
  1. use crate::async_utils::sleep;
  2. use crate::command_runner::{CommandRunner, SetuidCommandRunner};
  3. use crate::symbols::Symbol;
  4. use async_trait::async_trait;
  5. use std::error::Error;
  6. use std::ffi::OsStr;
  7. use std::path::Path;
  8. use std::time::Duration;
  9. #[derive(Debug)]
  10. pub struct UserService<'a, S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> {
  11. socket_path: S,
  12. service_name: &'a str,
  13. command_runner: SetuidCommandRunner<'a, U, R>,
  14. }
  15. impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'static, S, U, R> {
  16. pub fn new(
  17. socket_path: S,
  18. user_name: U,
  19. service_name: &'static str,
  20. command_runner: &'static R,
  21. ) -> Self {
  22. Self {
  23. socket_path,
  24. service_name,
  25. command_runner: SetuidCommandRunner::new(user_name, command_runner),
  26. }
  27. }
  28. }
  29. impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'_, S, U, R> {
  30. async fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
  31. let mut tries = 5;
  32. loop {
  33. let result = self.command_runner.run_with_args("systemctl", args).await?;
  34. if result.status.success() {
  35. return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
  36. } else {
  37. let raw_stderr = String::from_utf8(result.stderr)?;
  38. let stderr = raw_stderr.trim_end();
  39. if stderr != "Failed to connect to bus: No such file or directory" {
  40. return Err(stderr.into());
  41. }
  42. }
  43. tries -= 1;
  44. if tries == 0 {
  45. return Err("Gave up waiting for dbus to appear".to_string().into());
  46. }
  47. sleep(Duration::from_millis(500)).await;
  48. }
  49. }
  50. async fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
  51. loop {
  52. let active_state = self
  53. .systemctl_wait_for_dbus(args![
  54. "--user",
  55. "show",
  56. "--property",
  57. "ActiveState",
  58. self.service_name,
  59. ])
  60. .await?;
  61. match active_state.as_ref() {
  62. "ActiveState=activating" => sleep(Duration::from_millis(500)).await,
  63. "ActiveState=active" => return Ok(true),
  64. "ActiveState=failed" => {
  65. return Err(
  66. String::from_utf8(
  67. self
  68. .command_runner
  69. .get_output(
  70. "journalctl",
  71. args!["--user", format!("--user-unit={}", self.service_name)],
  72. )
  73. .await?,
  74. )?
  75. .into(),
  76. )
  77. }
  78. _ => return Ok(false),
  79. }
  80. }
  81. }
  82. }
  83. #[async_trait(?Send)]
  84. impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> Symbol for UserService<'_, S, U, R> {
  85. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  86. self.check_if_service().await
  87. }
  88. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  89. self
  90. .systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])
  91. .await?;
  92. self
  93. .systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])
  94. .await?;
  95. loop {
  96. if !(self.check_if_service().await?) {
  97. return Err("Generic error".into());
  98. }
  99. if self.socket_path.as_ref().exists() {
  100. return Ok(());
  101. }
  102. sleep(Duration::from_millis(500)).await;
  103. }
  104. }
  105. }