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.

213 lines
5.7 KiB

7 years ago
8 years ago
5 years ago
8 years ago
8 years ago
5 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 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
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
  1. use async_trait::async_trait;
  2. use std::error::Error;
  3. use std::ffi::OsStr;
  4. use std::io::Result as IoResult;
  5. use std::process::Output;
  6. use std::process::Stdio;
  7. use tokio::io::AsyncWriteExt;
  8. use tokio::process::Command;
  9. #[macro_export]
  10. macro_rules! args {
  11. ($($x:expr),*) => (
  12. &[$($x.as_ref()),*]
  13. );
  14. ($($x:expr,)*) => (args![$($x),*]) // handle trailing commas
  15. }
  16. fn check_success(output: Output) -> Result<Output, Box<dyn Error>> {
  17. if output.status.success() {
  18. Ok(output)
  19. } else {
  20. Err(String::from_utf8(output.stderr)?.into())
  21. }
  22. }
  23. pub fn is_success(res: Result<Output, impl Error + 'static>) -> Result<Output, Box<dyn Error>> {
  24. check_success(res?)
  25. }
  26. pub fn get_output(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
  27. Ok(check_success(output)?.stdout)
  28. }
  29. #[async_trait(?Send)]
  30. pub trait CommandRunner {
  31. async fn run(&self, program: &str, args: &[&OsStr], stdin: &str) -> IoResult<Output>;
  32. async fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult<Output> {
  33. self.run(program, args, "").await
  34. }
  35. async fn get_output(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
  36. let output = self.run_with_args(program, args).await?;
  37. get_output(output)
  38. }
  39. async fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box<dyn Error>> {
  40. is_success(self.run(program, args, "").await)?;
  41. Ok(())
  42. }
  43. async fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
  44. Ok(is_success(self.run_with_args(program, args).await)?.stderr)
  45. }
  46. }
  47. #[derive(Debug)]
  48. pub struct StdCommandRunner;
  49. #[async_trait(?Send)]
  50. impl CommandRunner for StdCommandRunner {
  51. async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
  52. //println!("{} {:?}", program, args);
  53. let mut child = Command::new(program)
  54. .args(args)
  55. .stdin(Stdio::piped())
  56. .stdout(Stdio::piped())
  57. .stderr(Stdio::piped())
  58. .spawn()
  59. .expect("Failed to spawn child process");
  60. let stdin = child.stdin.as_mut().expect("Failed to open stdin");
  61. stdin
  62. .write_all(input.as_bytes())
  63. .await
  64. .expect("Failed to write to stdin");
  65. let res = child.wait_with_output().await;
  66. //println!("{:?}", res);
  67. res
  68. }
  69. }
  70. #[derive(Debug)]
  71. pub struct SetuidCommandRunner<'a, U: AsRef<str>, C: CommandRunner> {
  72. command_runner: &'a C,
  73. user_name: U,
  74. }
  75. impl<'a, U: AsRef<str>, C: CommandRunner> SetuidCommandRunner<'a, U, C> {
  76. pub fn new(user_name: U, command_runner: &'a C) -> Self {
  77. SetuidCommandRunner {
  78. command_runner,
  79. user_name,
  80. }
  81. }
  82. }
  83. use std::env;
  84. use users::get_user_by_name;
  85. struct TempSetEnv<'a> {
  86. name: &'a str,
  87. old_value: Option<String>,
  88. }
  89. impl<'a> TempSetEnv<'a> {
  90. fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> {
  91. let old_value = env::var(name);
  92. env::set_var(name, new_value);
  93. TempSetEnv {
  94. name,
  95. old_value: old_value.ok(),
  96. }
  97. }
  98. }
  99. impl Drop for TempSetEnv<'_> {
  100. fn drop(&mut self) {
  101. match self.old_value {
  102. Some(ref val) => env::set_var(self.name, val),
  103. None => env::remove_var(self.name),
  104. }
  105. }
  106. }
  107. #[async_trait(?Send)]
  108. impl<U: AsRef<str>, C: CommandRunner> CommandRunner for SetuidCommandRunner<'_, U, C> {
  109. async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
  110. let uid = get_user_by_name(self.user_name.as_ref())
  111. .expect("User does not exist")
  112. .uid();
  113. let _set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name.as_ref()));
  114. let _set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
  115. //println!("{} {:?}", program, args);
  116. let mut child = Command::new(program)
  117. .args(args)
  118. .stdin(Stdio::piped())
  119. .uid(uid)
  120. .gid(uid)
  121. .stdout(Stdio::piped())
  122. .stderr(Stdio::piped())
  123. .spawn()
  124. .expect("Failed to spawn child process");
  125. let stdin = child.stdin.as_mut().expect("Failed to open stdin");
  126. stdin
  127. .write_all(input.as_bytes())
  128. .await
  129. .expect("Failed to write to stdin");
  130. let res = child.wait_with_output().await;
  131. //println!("{:?}", res);
  132. res
  133. }
  134. }
  135. #[derive(Debug)]
  136. pub struct SuCommandRunner<'a, C>
  137. where
  138. C: CommandRunner,
  139. {
  140. command_runner: &'a C,
  141. user_name: &'a str,
  142. }
  143. impl<'a, C> SuCommandRunner<'a, C>
  144. where
  145. C: 'a + CommandRunner,
  146. {
  147. pub fn new(user_name: &'a str, command_runner: &'a C) -> SuCommandRunner<'a, C> {
  148. SuCommandRunner {
  149. command_runner,
  150. user_name,
  151. }
  152. }
  153. }
  154. // Su doesn't set XDG_RUNTIME_DIR
  155. // https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
  156. #[async_trait(?Send)]
  157. impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
  158. where
  159. C: 'a + CommandRunner,
  160. {
  161. async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
  162. let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
  163. let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
  164. new_args.extend_from_slice(args);
  165. self.command_runner.run("su", &new_args, input).await
  166. }
  167. }
  168. #[cfg(test)]
  169. mod test {
  170. use crate::args;
  171. use crate::async_utils::run;
  172. use crate::command_runner::{CommandRunner, StdCommandRunner};
  173. use futures::future::FutureExt;
  174. use std::time::Instant;
  175. #[test]
  176. fn test() {
  177. let c = StdCommandRunner;
  178. run(async {
  179. let args = args!["1"];
  180. let start = Instant::now();
  181. let res = c.run("sleep", args, "").fuse();
  182. let ps = c.run("ps", args![], "").fuse();
  183. futures::pin_mut!(res, ps);
  184. loop {
  185. futures::select! {
  186. _ = res => {},
  187. _ = ps => assert!((Instant::now() - start).as_millis() < 1000),
  188. complete => break,
  189. }
  190. }
  191. })
  192. }
  193. }