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.

112 lines
3.1 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
3 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. }
  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. tries -= 1;
  43. if tries == 0 {
  44. return Err("Gave up waiting for dbus to appear".to_string().into());
  45. }
  46. sleep(Duration::from_millis(500)).await;
  47. }
  48. }
  49. async fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
  50. loop {
  51. let active_state = self
  52. .systemctl_wait_for_dbus(args![
  53. "--user",
  54. "show",
  55. "--property",
  56. "ActiveState",
  57. self.service_name,
  58. ])
  59. .await?;
  60. match active_state.as_ref() {
  61. "ActiveState=activating" => sleep(Duration::from_millis(500)).await,
  62. "ActiveState=active" => return Ok(true),
  63. "ActiveState=failed" => {
  64. return Err(
  65. String::from_utf8(
  66. self
  67. .command_runner
  68. .get_output(
  69. "journalctl",
  70. args!["--user", format!("--user-unit={}", self.service_name)],
  71. )
  72. .await?,
  73. )?
  74. .into(),
  75. )
  76. }
  77. _ => return Ok(false),
  78. }
  79. }
  80. }
  81. }
  82. #[async_trait(?Send)]
  83. impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> Symbol for UserService<'_, S, U, R> {
  84. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  85. self.check_if_service().await
  86. }
  87. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  88. self
  89. .systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])
  90. .await?;
  91. self
  92. .systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])
  93. .await?;
  94. loop {
  95. if !(self.check_if_service().await?) {
  96. return Err("Generic error".into());
  97. }
  98. if self.socket_path.as_ref().exists() {
  99. return Ok(());
  100. }
  101. sleep(Duration::from_millis(500)).await;
  102. }
  103. }
  104. }