| 
					
					
					
				 | 
				@ -1,4 +1,3 @@ | 
			
		
		
	
		
			
				 | 
				 | 
				use std::borrow::Cow;
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				use std::error::Error;
 | 
				 | 
				 | 
				use std::error::Error;
 | 
			
		
		
	
		
			
				 | 
				 | 
				use std::ffi::OsStr;
 | 
				 | 
				 | 
				use std::ffi::OsStr;
 | 
			
		
		
	
		
			
				 | 
				 | 
				use std::fmt;
 | 
				 | 
				 | 
				use std::fmt;
 | 
			
		
		
	
	
		
			
				| 
					
						
							
						
					
					
						
							
						
					
					
				 | 
				@ -46,29 +45,31 @@ impl<E: Error> Error for UserServiceError<E> { | 
			
		
		
	
		
			
				 | 
				 | 
				impl<E: Error> fmt::Display for UserServiceError<E> {
 | 
				 | 
				 | 
				impl<E: Error> fmt::Display for UserServiceError<E> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				 | 
				 | 
				  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    write!(f, "{}", self.description())?;
 | 
				 | 
				 | 
				    write!(f, "{}", self.description())?;
 | 
			
		
		
	
		
			
				 | 
				 | 
				    if let UserServiceError::ActivationFailed(Ok(ref log)) = self {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    if let Self::ActivationFailed(Ok(ref log)) = self {
 | 
			
		
		
	
		
			
				 | 
				 | 
				      write!(f, ": {:?}", log)?;
 | 
				 | 
				 | 
				      write!(f, ": {:?}", log)?;
 | 
			
		
		
	
		
			
				 | 
				 | 
				    };
 | 
				 | 
				 | 
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
				    Ok(())
 | 
				 | 
				 | 
				    Ok(())
 | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
				}
 | 
				 | 
				 | 
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				pub struct UserService<'a, C: AsRef<str>, R: CommandRunner> {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				  socket_path: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				pub struct UserService<'a, S: AsRef<Path>, U: AsRef<str>, C: AsRef<str>, R: CommandRunner> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  socket_path: S,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  service_name: &'a str,
 | 
				 | 
				 | 
				  service_name: &'a str,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  user_name: Cow<'a, str>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  user_name: U,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  command_runner: R,
 | 
				 | 
				 | 
				  command_runner: R,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  config: FileSymbol<C, PathBuf>,
 | 
				 | 
				 | 
				  config: FileSymbol<C, PathBuf>,
 | 
			
		
		
	
		
			
				 | 
				 | 
				}
 | 
				 | 
				 | 
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				impl<'a, R: CommandRunner> UserService<'a, String, SetuidCommandRunner<'a, Cow<'a, str>, R>> {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				impl<'a, S: AsRef<Path>, U: AsRef<str> + Clone, R: CommandRunner>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  UserService<'a, S, U, String, SetuidCommandRunner<'a, U, R>>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
				  pub fn new_nodejs(
 | 
				 | 
				 | 
				  pub fn new_nodejs(
 | 
			
		
		
	
		
			
				 | 
				 | 
				    home: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				    user_name: Cow<'a, str>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    home: &'_ Path,
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    user_name: U,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    service_name: &'a str,
 | 
				 | 
				 | 
				    service_name: &'a str,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    path: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    path: &'_ Path,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    command_runner: &'a R,
 | 
				 | 
				 | 
				    command_runner: &'a R,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    socket_path: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    socket_path: S,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  ) -> Self {
 | 
				 | 
				 | 
				  ) -> Self {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    let content = format!(
 | 
				 | 
				 | 
				    let content = format!(
 | 
			
		
		
	
		
			
				 | 
				 | 
				      "[Service]
 | 
				 | 
				 | 
				      "[Service]
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -88,9 +89,9 @@ Restart=always | 
			
		
		
	
		
			
				 | 
				 | 
				WantedBy=default.target
 | 
				 | 
				 | 
				WantedBy=default.target
 | 
			
		
		
	
		
			
				 | 
				 | 
				",
 | 
				 | 
				 | 
				",
 | 
			
		
		
	
		
			
				 | 
				 | 
				      path.to_str().unwrap(),
 | 
				 | 
				 | 
				      path.to_str().unwrap(),
 | 
			
		
		
	
		
			
				 | 
				 | 
				      socket_path.to_str().unwrap()
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      socket_path.as_ref().to_str().unwrap()
 | 
			
		
		
	
		
			
				 | 
				 | 
				    );
 | 
				 | 
				 | 
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
				    UserService::new(
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    Self::new(
 | 
			
		
		
	
		
			
				 | 
				 | 
				      socket_path,
 | 
				 | 
				 | 
				      socket_path,
 | 
			
		
		
	
		
			
				 | 
				 | 
				      home,
 | 
				 | 
				 | 
				      home,
 | 
			
		
		
	
		
			
				 | 
				 | 
				      user_name,
 | 
				 | 
				 | 
				      user_name,
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -101,15 +102,15 @@ WantedBy=default.target | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				  pub fn new(
 | 
				 | 
				 | 
				  pub fn new(
 | 
			
		
		
	
		
			
				 | 
				 | 
				    socket_path: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				    home: Cow<'a, Path>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				    user_name: Cow<'a, str>,
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    socket_path: S,
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    home: &'_ Path,
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    user_name: U,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    service_name: &'a str,
 | 
				 | 
				 | 
				    service_name: &'a str,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    command_runner: &'a R,
 | 
				 | 
				 | 
				    command_runner: &'a R,
 | 
			
		
		
	
		
			
				 | 
				 | 
				    content: String,
 | 
				 | 
				 | 
				    content: String,
 | 
			
		
		
	
		
			
				 | 
				 | 
				  ) -> Self {
 | 
				 | 
				 | 
				  ) -> Self {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    let config_path: PathBuf = [
 | 
				 | 
				 | 
				    let config_path: PathBuf = [
 | 
			
		
		
	
		
			
				 | 
				 | 
				      home.as_ref(),
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      home,
 | 
			
		
		
	
		
			
				 | 
				 | 
				      format!(".config/systemd/user/{}.service", service_name).as_ref(),
 | 
				 | 
				 | 
				      format!(".config/systemd/user/{}.service", service_name).as_ref(),
 | 
			
		
		
	
		
			
				 | 
				 | 
				    ]
 | 
				 | 
				 | 
				    ]
 | 
			
		
		
	
		
			
				 | 
				 | 
				    .iter()
 | 
				 | 
				 | 
				    .iter()
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -125,19 +126,21 @@ WantedBy=default.target | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
				}
 | 
				 | 
				 | 
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				impl<'a, C: AsRef<str>, R: CommandRunner> UserService<'a, C, R> {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				impl<'a, S: AsRef<Path>, U: AsRef<str>, C: AsRef<str>, R: CommandRunner>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  UserService<'a, S, U, C, R>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
				  fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
 | 
				 | 
				 | 
				  fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    let mut tries = 5;
 | 
				 | 
				 | 
				    let mut tries = 5;
 | 
			
		
		
	
		
			
				 | 
				 | 
				    loop {
 | 
				 | 
				 | 
				    loop {
 | 
			
		
		
	
		
			
				 | 
				 | 
				      let result = self.command_runner.run_with_args("systemctl", args)?;
 | 
				 | 
				 | 
				      let result = self.command_runner.run_with_args("systemctl", args)?;
 | 
			
		
		
	
		
			
				 | 
				 | 
				      if !result.status.success() {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      if result.status.success() {
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				        return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
				        let raw_stderr = String::from_utf8(result.stderr)?;
 | 
				 | 
				 | 
				        let raw_stderr = String::from_utf8(result.stderr)?;
 | 
			
		
		
	
		
			
				 | 
				 | 
				        let stderr = raw_stderr.trim_end();
 | 
				 | 
				 | 
				        let stderr = raw_stderr.trim_end();
 | 
			
		
		
	
		
			
				 | 
				 | 
				        if stderr != "Failed to connect to bus: No such file or directory" {
 | 
				 | 
				 | 
				        if stderr != "Failed to connect to bus: No such file or directory" {
 | 
			
		
		
	
		
			
				 | 
				 | 
				          return Err(stderr.into());
 | 
				 | 
				 | 
				          return Err(stderr.into());
 | 
			
		
		
	
		
			
				 | 
				 | 
				        }
 | 
				 | 
				 | 
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
				      } else {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				        return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				      }
 | 
				 | 
				 | 
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
				      tries -= 1;
 | 
				 | 
				 | 
				      tries -= 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
				      if tries == 0 {
 | 
				 | 
				 | 
				      if tries == 0 {
 | 
			
		
		
	
	
		
			
				| 
					
						
							
						
					
					
						
							
						
					
					
				 | 
				@ -173,7 +176,9 @@ impl<'a, C: AsRef<str>, R: CommandRunner> UserService<'a, C, R> { | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
				}
 | 
				 | 
				 | 
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				impl<'a, C: AsRef<str>, R: CommandRunner> Symbol for UserService<'a, C, R> {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				impl<'a, S: AsRef<Path>, U: AsRef<str>, C: AsRef<str>, R: CommandRunner> Symbol
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  for UserService<'a, S, U, C, R>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
				  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
				 | 
				  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    if !(self.config.target_reached()?) {
 | 
				 | 
				 | 
				    if !(self.config.target_reached()?) {
 | 
			
		
		
	
		
			
				 | 
				 | 
				      return Ok(false);
 | 
				 | 
				 | 
				      return Ok(false);
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -193,7 +198,7 @@ impl<'a, C: AsRef<str>, R: CommandRunner> Symbol for UserService<'a, C, R> { | 
			
		
		
	
		
			
				 | 
				 | 
				        ));
 | 
				 | 
				 | 
				        ));
 | 
			
		
		
	
		
			
				 | 
				 | 
				      }
 | 
				 | 
				 | 
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				      if self.socket_path.exists() {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      if self.socket_path.as_ref().exists() {
 | 
			
		
		
	
		
			
				 | 
				 | 
				        return Ok(());
 | 
				 | 
				 | 
				        return Ok(());
 | 
			
		
		
	
		
			
				 | 
				 | 
				      }
 | 
				 | 
				 | 
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
				      sleep(Duration::from_millis(500));
 | 
				 | 
				 | 
				      sleep(Duration::from_millis(500));
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -203,7 +208,7 @@ impl<'a, C: AsRef<str>, R: CommandRunner> Symbol for UserService<'a, C, R> { | 
			
		
		
	
		
			
				 | 
				 | 
				  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				 | 
				 | 
				  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    let mut r = vec![Resource::new(
 | 
				 | 
				 | 
				    let mut r = vec![Resource::new(
 | 
			
		
		
	
		
			
				 | 
				 | 
				      "file",
 | 
				 | 
				 | 
				      "file",
 | 
			
		
		
	
		
			
				 | 
				 | 
				      format!("/var/lib/systemd/linger/{}", self.user_name),
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      format!("/var/lib/systemd/linger/{}", self.user_name.as_ref()),
 | 
			
		
		
	
		
			
				 | 
				 | 
				    )];
 | 
				 | 
				 | 
				    )];
 | 
			
		
		
	
		
			
				 | 
				 | 
				    r.extend(self.config.get_prerequisites().into_iter());
 | 
				 | 
				 | 
				    r.extend(self.config.get_prerequisites().into_iter());
 | 
			
		
		
	
		
			
				 | 
				 | 
				    r
 | 
				 | 
				 | 
				    r
 | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -221,7 +226,9 @@ impl<'a, C: AsRef<str>, R: CommandRunner> Symbol for UserService<'a, C, R> { | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
				}
 | 
				 | 
				 | 
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				impl<'a, C: AsRef<str>, R: CommandRunner> fmt::Display for UserService<'a, C, R> {
 | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				impl<'a, S: AsRef<Path>, U: AsRef<str>, C: AsRef<str>, R: CommandRunner> fmt::Display
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  for UserService<'a, S, U, C, R>
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
				  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				 | 
				 | 
				  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
			
		
		
	
		
			
				 | 
				 | 
				    write!(f, "Systemd user service unit for {}", self.service_name)
 | 
				 | 
				 | 
				    write!(f, "Systemd user service unit for {}", self.service_name)
 | 
			
		
		
	
		
			
				 | 
				 | 
				  }
 | 
				 | 
				 | 
				  }
 | 
			
		
		
	
	
		
			
				| 
					
					
					
				 | 
				
  |