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.

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