| 
						
						
						
					 | 
				
				 | 
				
					@ -1,4 +1,3 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::borrow::Cow;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::error::Error;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::ffi::OsStr;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::fmt;
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -46,29 +45,31 @@ impl<E: Error> Error for UserServiceError<E> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					impl<E: Error> fmt::Display for UserServiceError<E> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    write!(f, "{}", self.description())?;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if let UserServiceError::ActivationFailed(Ok(ref log)) = self {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if let Self::ActivationFailed(Ok(ref log)) = self {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      write!(f, ": {:?}", log)?;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    };
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    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,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  user_name: Cow<'a, str>,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  user_name: U,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  command_runner: R,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  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(
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    home: Cow<'a, Path>,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    user_name: Cow<'a, str>,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    home: &'_ Path,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    user_name: U,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    service_name: &'a str,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    path: Cow<'a, Path>,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    path: &'_ Path,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    command_runner: &'a R,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    socket_path: Cow<'a, Path>,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    socket_path: S,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ) -> Self {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let content = format!(
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "[Service]
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -88,9 +89,9 @@ Restart=always | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					WantedBy=default.target
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      path.to_str().unwrap(),
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      socket_path.to_str().unwrap()
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      socket_path.as_ref().to_str().unwrap()
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    );
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    UserService::new(
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    Self::new(
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      socket_path,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      home,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      user_name,
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -101,15 +102,15 @@ WantedBy=default.target | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  }
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  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,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    command_runner: &'a R,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    content: String,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ) -> Self {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let config_path: PathBuf = [
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      home.as_ref(),
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      home,
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      format!(".config/systemd/user/{}.service", service_name).as_ref(),
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ]
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    .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>> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut tries = 5;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    loop {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      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 stderr = raw_stderr.trim_end();
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if stderr != "Failed to connect to bus: No such file or directory" {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          return Err(stderr.into());
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } else {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      tries -= 1;
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      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>> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if !(self.config.target_reached()?) {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      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(());
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      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> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut r = vec![Resource::new(
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "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
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -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> {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    write!(f, "Systemd user service unit for {}", self.service_name)
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  }
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
				 | 
				
					
  |