CH-J Toolsуправление серверами через SSH
Меню
Публикуемый источник

CH-J Tools

Просмотр каталогов и файлов для конкретного выпуска приложения.

Загрузка источника ZIP
CH-J / Server Manager 1704 файлы
0.1.2-beta Добавлено: · Построено:
launcher.rs Добавлено: Дата нерегистрации 6,2 KB · 126 Линии
Код Загрузить файл
1//! Installer hand-off (port of `updates/updateInstallerLauncher.js`).
2//! Windows/macOS: the verified EXE/DMG is opened by the OS (launched ≠ installed).
3//! Linux: fixed `/usr/bin/pkexec /usr/bin/apt-get install --reinstall --yes <verified .deb>`,
4//! no shell, root-owned non-writable executables, 30 min timeout.
6use crate::core::error::{CoreError, CoreResult};
7use std::path::Path;
8use std::time::Duration;
10pub const PKEXEC_PATH: &str = "/usr/bin/pkexec";
11pub const APT_GET_PATH: &str = "/usr/bin/apt-get";
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct LaunchResult {
15 pub launched: bool,
16 pub installed: bool,
17 pub restart: bool,
20fn install_error(code: &str, message: &str) -> CoreError {
21 CoreError::new(code, message)
24pub fn assert_safe_regular_file(path: &Path, label: &str) -> CoreResult<std::fs::Metadata> {
25 if !path.is_absolute() {
26 return Err(install_error("UPDATE_INSTALL_UNSAFE_PATH", &format!("{label} path must be absolute.")));
27 }
28 let meta = std::fs::symlink_metadata(path).map_err(|_| install_error("UPDATE_INSTALL_FILE_MISSING", &format!("{label} is missing.")))?;
29 #[cfg(unix)]
30 let links = {
31 use std::os::unix::fs::MetadataExt;
32 meta.nlink()
33 };
34 #[cfg(not(unix))]
35 let links = 1;
36 if !meta.is_file() || meta.file_type().is_symlink() || links != 1 || meta.len() == 0 {
37 return Err(install_error("UPDATE_INSTALL_UNSAFE_PATH", &format!("{label} must be a non-empty regular file.")));
38 }
39 Ok(meta)
42#[cfg(unix)]
43pub fn assert_trusted_system_executable(path: &Path) -> CoreResult<()> {
44 use std::os::unix::fs::MetadataExt;
45 let meta = assert_safe_regular_file(path, "System installer executable")?;
46 if meta.uid() != 0 {
47 return Err(install_error("UPDATE_INSTALL_UNSAFE_EXECUTABLE", "System installer executable is not owned by root."));
48 }
49 if meta.mode() & 0o111 == 0 {
50 return Err(install_error("UPDATE_INSTALL_UNSAFE_EXECUTABLE", "System installer executable is not executable."));
51 }
52 if meta.mode() & 0o022 != 0 {
53 return Err(install_error("UPDATE_INSTALL_UNSAFE_EXECUTABLE", "System installer executable is writable by an untrusted account."));
54 }
55 Ok(())
58pub async fn install(app: &tauri::AppHandle, path: &Path, logger: &crate::core::logging::Logger) -> CoreResult<LaunchResult> {
59 if cfg!(target_os = "linux") {
60 return install_deb(path, logger).await;
61 }
62 use tauri_plugin_opener::OpenerExt;
63 app.opener().open_path(path.to_string_lossy(), None::<&str>).map_err(|e| install_error("UPDATE_INSTALL_FAILED", &format!("The installer could not be opened: {e}")))?;
64 Ok(LaunchResult { launched: true, installed: false, restart: false })
67#[cfg(unix)]
68async fn install_deb(path: &Path, logger: &crate::core::logging::Logger) -> CoreResult<LaunchResult> {
69 if !path.is_absolute() {
70 return Err(install_error("UPDATE_INSTALL_UNSAFE_PATH", "Verified Debian update package path must be absolute."));
71 }
72 if path.extension().map(|e| e.to_string_lossy().to_lowercase()) != Some("deb".into()) {
73 return Err(install_error("UPDATE_INSTALL_UNSUPPORTED_PACKAGE", "Linux updates must use a Debian package."));
74 }
75 assert_safe_regular_file(path, "Verified Debian update package")?;
76 assert_trusted_system_executable(Path::new(PKEXEC_PATH))?;
77 assert_trusted_system_executable(Path::new(APT_GET_PATH))?;
78 let name = path.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default();
79 logger.info("Starting privileged Debian update installation.", serde_json::json!({ "filename": name }));
80 let mut command = tokio::process::Command::new(PKEXEC_PATH);
81 command.args([APT_GET_PATH, "install", "--reinstall", "--yes"]).arg(path).stdin(std::process::Stdio::null()).stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped()).kill_on_drop(true);
82 let child = command.spawn().map_err(|_| install_error("UPDATE_INSTALL_LAUNCH_FAILED", "The privileged installer could not be started."))?;
83 let output = tokio::time::timeout(Duration::from_secs(30 * 60), child.wait_with_output())
84 .await
85 .map_err(|_| install_error("UPDATE_INSTALL_TIMEOUT", "The Debian update installation timed out."))?
86 .map_err(|_| install_error("UPDATE_INSTALL_LAUNCH_FAILED", "The privileged installer could not be started."))?;
87 if output.status.code() == Some(0) {
88 logger.info("Debian update installation completed.", serde_json::json!({ "filename": name }));
89 return Ok(LaunchResult { launched: true, installed: true, restart: true });
90 }
91 let denied = matches!(output.status.code(), Some(126) | Some(127));
92 let text = format!("{}\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr));
93 let tail: String = text.trim().chars().rev().take(4096).collect::<Vec<_>>().into_iter().rev().collect();
94 logger.warn("Debian update installation failed.", serde_json::json!({ "code": output.status.code(), "output": tail }));
95 Err(if denied {
96 install_error("UPDATE_INSTALL_AUTHORIZATION_DENIED", "Administrator authorization for the update was canceled or denied.")
97 } else {
98 install_error("UPDATE_INSTALL_FAILED", "The Debian update installation failed.")
99 })
102#[cfg(not(unix))]
103async fn install_deb(_path: &Path, _logger: &crate::core::logging::Logger) -> CoreResult<LaunchResult> {
104 Err(install_error("UPDATE_INSTALL_UNSUPPORTED_PACKAGE", "Linux updates must use a Debian package."))
107#[cfg(all(test, unix))]
108mod tests {
109 use super::*;
111 #[test]
112 fn unsafe_files_rejected() {
113 let dir = tempfile::tempdir().unwrap();
114 assert_eq!(assert_safe_regular_file(Path::new("relative.deb"), "x").unwrap_err().code, "UPDATE_INSTALL_UNSAFE_PATH");
115 let file = dir.path().join("a.deb");
116 std::fs::write(&file, b"x").unwrap();
117 let link = dir.path().join("b.deb");
118 std::os::unix::fs::symlink(&file, &link).unwrap();
119 assert!(assert_safe_regular_file(&file, "x").is_ok());
120 assert!(assert_safe_regular_file(&link, "x").is_err());
121 // A file owned by the current (non-root) user is not a trusted system executable.
122 if unsafe { libc::getuid() } != 0 {
123 assert_eq!(assert_trusted_system_executable(&file).unwrap_err().code, "UPDATE_INSTALL_UNSAFE_EXECUTABLE");
124 }
125 }
SHA-256ff6c1a3267969b2be18e54a3e5c602f72f7085007412e9529ea3e856784fa8e9

CH-J Proprietary Software License 1.14 · Источник представлен в CH-J Proprietary Software License 1.14. Его наличие не изменяет лицензионных условий и не предоставляет дополнительных прав.

Архив SHA-256ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
Файлы