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.

89 lines
2.1 KiB

8 years ago
8 years ago
5 years ago
5 years ago
8 years ago
5 years ago
5 years ago
7 years ago
8 years ago
4 years ago
4 years ago
8 years ago
8 years ago
5 years ago
5 years ago
8 years ago
5 years ago
5 years ago
8 years ago
  1. use crate::command_runner::CommandRunner;
  2. use crate::symbols::Symbol;
  3. use async_trait::async_trait;
  4. use once_cell::sync::Lazy;
  5. use std::error::Error;
  6. use tokio::sync::Semaphore;
  7. use std::path::Path;
  8. pub type Wait = Lazy<Semaphore>;
  9. static WAIT: Wait = Lazy::new(|| Semaphore::new(1));
  10. #[derive(Debug)]
  11. pub struct User<U, H, C> {
  12. user_name: U,
  13. home_path: H,
  14. command_runner: C,
  15. }
  16. impl<U, H, C> User<U, H, C> {
  17. pub const fn new(user_name: U, home_path: H, command_runner: C) -> Self {
  18. Self {
  19. user_name,
  20. home_path,
  21. command_runner,
  22. }
  23. }
  24. }
  25. #[async_trait(?Send)]
  26. impl<U: AsRef<str>, H: AsRef<Path>, C: CommandRunner> Symbol for User<U, H, C> {
  27. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  28. let output = self
  29. .command_runner
  30. .run_with_args("getent", args!["passwd", self.user_name.as_ref()])
  31. .await?;
  32. match output.status.code() {
  33. Some(2) => Ok(false),
  34. Some(0) => Ok(true),
  35. _ => Err("Unknown error".into()),
  36. }
  37. }
  38. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  39. // adduser is not reentrant because finding the next uid
  40. // and creating the account is not an atomic operation
  41. let wait = WAIT.acquire().await;
  42. let res = self
  43. .command_runner
  44. .run_successfully(
  45. "adduser",
  46. args![
  47. // "-m", // Necessary for Fedora, not accepted in Debian
  48. "--system",
  49. "--home",
  50. self.home_path.as_ref(),
  51. self.user_name.as_ref(),
  52. ],
  53. )
  54. .await;
  55. drop(wait);
  56. res
  57. }
  58. }
  59. #[cfg(test)]
  60. mod test {
  61. use crate::async_utils::run;
  62. use crate::command_runner::StdCommandRunner;
  63. use crate::symbols::user::User;
  64. use crate::symbols::Symbol;
  65. #[test]
  66. fn test_target_reached_nonexisting() {
  67. let symbol = User {
  68. user_name: "nonexisting",
  69. command_runner: StdCommandRunner,
  70. };
  71. assert_eq!(run(symbol.target_reached()).unwrap(), false);
  72. }
  73. #[test]
  74. fn test_target_reached_root() {
  75. let symbol = User {
  76. user_name: "root",
  77. command_runner: StdCommandRunner,
  78. };
  79. assert_eq!(run(symbol.target_reached()).unwrap(), true);
  80. }
  81. }