feat: add cross-platform support via platform detection module
Introduces src/platform.rs with OS detection and env var overrides. All hardcoded Linux paths replaced with Platform::detect() across 8 source files. Key changes: - New Platform struct with 11 fields (all overridable via env vars) - /proc/ access gated to Linux (#[cfg(target_os = "linux")]) - pgrep/pkill patterns broadened for cross-platform LS discovery - sec-ch-ua-platform header now dynamic per OS - Token, traces, config, CA cert paths use platform module - LD_PRELOAD DNS redirect gated to Linux only - Setup scripts for Linux (systemd) and macOS (launchd) - find_ls_binary_path has cross-platform stubs All 46 tests pass, cargo check clean.
This commit is contained in:
70
scripts/setup-linux.sh
Executable file
70
scripts/setup-linux.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# ZeroGravity — Linux setup
|
||||
# Creates the zerogravity-ls system user for UID-scoped iptables isolation,
|
||||
# installs the systemd user service, and builds the dns_redirect.so preload lib.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# ── 1. System user for UID isolation ──
|
||||
echo "→ Creating zerogravity-ls system user…"
|
||||
if id -u zerogravity-ls &>/dev/null; then
|
||||
echo " Already exists."
|
||||
else
|
||||
sudo useradd --system --no-create-home --shell /usr/sbin/nologin zerogravity-ls
|
||||
echo " Created."
|
||||
fi
|
||||
|
||||
# ── 2. Sudoers rule (run commands as zerogravity-ls without password) ──
|
||||
SUDOERS="/etc/sudoers.d/zerogravity"
|
||||
echo "→ Installing sudoers rule…"
|
||||
if [ -f "$SUDOERS" ]; then
|
||||
echo " Already exists."
|
||||
else
|
||||
echo "$USER ALL=(zerogravity-ls) NOPASSWD: ALL" | sudo tee "$SUDOERS" > /dev/null
|
||||
sudo chmod 0440 "$SUDOERS"
|
||||
echo " Installed: $SUDOERS"
|
||||
fi
|
||||
|
||||
# ── 3. Data directory permissions ──
|
||||
echo "→ Setting up /tmp/zerogravity-standalone…"
|
||||
sudo mkdir -p /tmp/zerogravity-standalone
|
||||
sudo chmod 1777 /tmp/zerogravity-standalone
|
||||
|
||||
# ── 4. Config directory ──
|
||||
echo "→ Setting up ~/.config/zerogravity…"
|
||||
mkdir -p "$HOME/.config/zerogravity"
|
||||
|
||||
# ── 5. Systemd user service ──
|
||||
echo "→ Installing systemd user service…"
|
||||
UNIT_DIR="$HOME/.config/systemd/user"
|
||||
mkdir -p "$UNIT_DIR"
|
||||
cat > "$UNIT_DIR/zerogravity.service" << EOF
|
||||
[Unit]
|
||||
Description=ZeroGravity Proxy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=$PROJECT_DIR/target/release/zerogravity
|
||||
WorkingDirectory=$PROJECT_DIR
|
||||
Environment=RUST_LOG=info
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
systemctl --user daemon-reload
|
||||
echo " Installed: $UNIT_DIR/zerogravity.service"
|
||||
echo " Enable with: systemctl --user enable zerogravity"
|
||||
|
||||
# ── 6. Build ──
|
||||
echo "→ Building release binary…"
|
||||
cd "$PROJECT_DIR"
|
||||
cargo build --release 2>&1 | tail -1
|
||||
echo ""
|
||||
echo "✓ Setup complete. Start with: zg start"
|
||||
65
scripts/setup-macos.sh
Executable file
65
scripts/setup-macos.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# ZeroGravity — macOS setup
|
||||
# Installs a launchd plist for automatic startup and sets up config directories.
|
||||
# No UID isolation on macOS — runs in headless/HTTPS_PROXY mode only.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
CONFIG_DIR="$HOME/Library/Application Support/zerogravity"
|
||||
|
||||
# ── 1. Config directory ──
|
||||
echo "→ Setting up config directory…"
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
# ── 2. Data directory ──
|
||||
echo "→ Setting up /tmp/zerogravity-standalone…"
|
||||
mkdir -p /tmp/zerogravity-standalone
|
||||
|
||||
# ── 3. Launchd plist ──
|
||||
echo "→ Installing launchd plist…"
|
||||
PLIST_DIR="$HOME/Library/LaunchAgents"
|
||||
PLIST="$PLIST_DIR/com.zerogravity.proxy.plist"
|
||||
mkdir -p "$PLIST_DIR"
|
||||
cat > "$PLIST" << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.zerogravity.proxy</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>$PROJECT_DIR/target/release/zerogravity</string>
|
||||
</array>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>$PROJECT_DIR</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>RUST_LOG</key>
|
||||
<string>info</string>
|
||||
</dict>
|
||||
<key>KeepAlive</key>
|
||||
<dict>
|
||||
<key>SuccessfulExit</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>StandardOutPath</key>
|
||||
<string>$HOME/Library/Logs/zerogravity.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>$HOME/Library/Logs/zerogravity.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
echo " Installed: $PLIST"
|
||||
echo " Start with: launchctl load $PLIST"
|
||||
echo " Stop with: launchctl unload $PLIST"
|
||||
|
||||
# ── 4. Build ──
|
||||
echo "→ Building release binary…"
|
||||
cd "$PROJECT_DIR"
|
||||
cargo build --release 2>&1 | tail -1
|
||||
echo ""
|
||||
echo "✓ Setup complete."
|
||||
echo " Start with: launchctl load $PLIST"
|
||||
echo " Or manually: zg start"
|
||||
@@ -51,7 +51,7 @@ static STATIC_HEADERS: LazyLock<HeaderMap> = LazyLock::new(|| {
|
||||
h.insert(HeaderName::from_static("sec-ch-ua-mobile"), hv("?0"));
|
||||
h.insert(
|
||||
HeaderName::from_static("sec-ch-ua-platform"),
|
||||
hv("\"Linux\""),
|
||||
hv(&format!("\"{}\"", crate::platform::Platform::detect().os_name)),
|
||||
);
|
||||
h.insert("Sec-Fetch-Dest", hv("empty"));
|
||||
h.insert("Sec-Fetch-Mode", hv("cors"));
|
||||
@@ -499,12 +499,11 @@ impl Backend {
|
||||
|
||||
fn discover() -> Result<BackendInner, String> {
|
||||
// Try to find the real LS binary first (when MITM wrapper is installed,
|
||||
// the wrapper is a shell script named language_server_linux_x64, while
|
||||
// the real binary is language_server_linux_x64.real)
|
||||
// the wrapper is a shell script, while the real binary has .real suffix)
|
||||
let pid_output = Command::new("sh")
|
||||
.args([
|
||||
"-c",
|
||||
"pgrep -f 'language_server_linux_x64\\.real' | head -1",
|
||||
"pgrep -f 'language_server.*\\.real' | head -1",
|
||||
])
|
||||
.output()
|
||||
.map_err(|e| format!("pgrep failed: {e}"))?;
|
||||
@@ -513,10 +512,10 @@ fn discover() -> Result<BackendInner, String> {
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
// Fallback: find any language_server_linux process
|
||||
// Fallback: find any language_server process
|
||||
if pid.is_empty() {
|
||||
let pid_output = Command::new("sh")
|
||||
.args(["-c", "pgrep -f language_server_linux | head -1"])
|
||||
.args(["-c", "pgrep -f language_server | head -1"])
|
||||
.output()
|
||||
.map_err(|e| format!("pgrep failed: {e}"))?;
|
||||
pid = String::from_utf8_lossy(&pid_output.stdout)
|
||||
@@ -611,8 +610,7 @@ fn discover() -> Result<BackendInner, String> {
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| {
|
||||
let home = std::env::var("HOME").unwrap_or_default();
|
||||
let path = format!("{home}/.config/zerogravity/token");
|
||||
let path = token_file_path();
|
||||
fs::read_to_string(&path)
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
|
||||
@@ -21,26 +21,54 @@ struct DetectedVersions {
|
||||
/// back to its binary, then walking up to the app root. Falls back to
|
||||
/// well-known install paths.
|
||||
fn find_install_dir() -> Option<String> {
|
||||
// 1. Try tracing the running language server → /usr/share/antigravity/resources/app/extensions/...
|
||||
let p = crate::platform::Platform::detect();
|
||||
|
||||
// 1. Check if platform-detected app_root exists
|
||||
let app_root_parent = std::path::Path::new(&p.app_root)
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.to_string_lossy().to_string());
|
||||
if let Some(ref dir) = app_root_parent {
|
||||
if fs::metadata(format!("{dir}/resources/app/product.json")).is_ok() {
|
||||
return Some(dir.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Try tracing the running language server via /proc (Linux only)
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(output) = Command::new("sh")
|
||||
.args(["-c", "pgrep -f language_server_linux | head -1"])
|
||||
.args(["-c", "pgrep -f language_server | head -1"])
|
||||
.output()
|
||||
{
|
||||
let pid = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if !pid.is_empty() {
|
||||
if let Ok(exe) = fs::read_link(format!("/proc/{pid}/exe")) {
|
||||
let exe_str = exe.to_string_lossy().to_string();
|
||||
// exe is like: /usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64
|
||||
// We want: /usr/share/antigravity
|
||||
if let Some(idx) = exe_str.find("/resources/") {
|
||||
return Some(exe_str[..idx].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fall back to well-known install paths
|
||||
for path in &["/usr/share/antigravity", "/opt/Antigravity"] {
|
||||
// 3. Fall back to well-known install paths
|
||||
#[cfg(target_os = "linux")]
|
||||
let candidates = ["/usr/share/antigravity", "/opt/Antigravity"];
|
||||
#[cfg(target_os = "macos")]
|
||||
let candidates = [
|
||||
"/Applications/Antigravity.app/Contents",
|
||||
&format!("{}/Applications/Antigravity.app/Contents", std::env::var("HOME").unwrap_or_default()),
|
||||
];
|
||||
#[cfg(target_os = "windows")]
|
||||
let candidates = [
|
||||
&format!("{}\\Programs\\Antigravity", std::env::var("LOCALAPPDATA").unwrap_or_default()),
|
||||
];
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
let candidates: [&str; 0] = [];
|
||||
|
||||
for path in &candidates {
|
||||
if fs::metadata(format!("{path}/resources/app/product.json")).is_ok() {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
@@ -178,14 +206,23 @@ pub const LS_SERVICE: &str = "exa.language_server_pb.LanguageServerService";
|
||||
|
||||
/// Log base directory for Antigravity.
|
||||
pub fn log_base() -> String {
|
||||
let p = crate::platform::Platform::detect();
|
||||
// Antigravity logs live next to its state DB
|
||||
let state_parent = std::path::Path::new(&p.state_db_path)
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
|
||||
format!("{home}/.config/Antigravity/logs")
|
||||
format!("{home}/.config/Antigravity")
|
||||
});
|
||||
format!("{state_parent}/logs")
|
||||
}
|
||||
|
||||
/// Token file path.
|
||||
pub fn token_file_path() -> String {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
|
||||
format!("{home}/.config/zerogravity/token")
|
||||
crate::platform::Platform::detect().token_path.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
/// User-Agent string matching the Electron webview — computed once.
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@@ -8,6 +8,7 @@ mod api;
|
||||
mod backend;
|
||||
mod constants;
|
||||
mod mitm;
|
||||
mod platform;
|
||||
mod proto;
|
||||
mod quota;
|
||||
mod session;
|
||||
@@ -123,7 +124,7 @@ async fn main() {
|
||||
.status();
|
||||
// Also kill any leftover standalone LS processes
|
||||
let _ = std::process::Command::new("pkill")
|
||||
.args(["-f", "language_server_linux.*antigravity-standalone"])
|
||||
.args(["-f", "language_server.*antigravity-standalone"])
|
||||
.status();
|
||||
// Retry once
|
||||
match tokio::net::TcpListener::bind(&addr).await {
|
||||
@@ -240,8 +241,7 @@ async fn main() {
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| {
|
||||
let home = std::env::var("HOME").unwrap_or_default();
|
||||
let path = format!("{home}/.config/zerogravity/token");
|
||||
let path = crate::constants::token_file_path();
|
||||
std::fs::read_to_string(&path)
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
@@ -483,6 +483,7 @@ fn check_wrapper_installed() -> bool {
|
||||
}
|
||||
|
||||
/// Find the LS binary path by reading /proc/<pid>/exe for known language server processes.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn find_ls_binary_path() -> Option<String> {
|
||||
// Try all running processes, look for ones that look like the LS
|
||||
let proc = std::path::Path::new("/proc");
|
||||
@@ -505,6 +506,8 @@ fn find_ls_binary_path() -> Option<String> {
|
||||
let target_clean = target_str.trim_end_matches(" (deleted)");
|
||||
// Match any binary that looks like the Antigravity LS
|
||||
if target_clean.contains("language_server_linux")
|
||||
|| target_clean.contains("language_server_darwin")
|
||||
|| target_clean.contains("language_server_windows")
|
||||
|| target_clean.contains("antigravity-language-server")
|
||||
{
|
||||
// Strip .real suffix — if the wrapper exec'd the backup, we want the base name
|
||||
@@ -517,10 +520,12 @@ fn find_ls_binary_path() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn find_ls_binary_path() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get the data directory for storing MITM CA cert/key.
|
||||
fn dirs_data_dir() -> std::path::PathBuf {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||
std::path::PathBuf::from(home)
|
||||
.join(".config")
|
||||
.join("zerogravity")
|
||||
crate::platform::Platform::detect().config_dir
|
||||
}
|
||||
|
||||
316
src/platform.rs
Normal file
316
src/platform.rs
Normal file
@@ -0,0 +1,316 @@
|
||||
//! Platform detection and path resolution.
|
||||
//!
|
||||
//! All platform-specific paths are resolved here. Every path can be overridden
|
||||
//! via environment variables, falling back to OS-specific defaults.
|
||||
//!
|
||||
//! # Env Var Overrides
|
||||
//!
|
||||
//! | Variable | Description |
|
||||
//! |----------|-------------|
|
||||
//! | `ZEROGRAVITY_LS_PATH` | Path to the Language Server binary |
|
||||
//! | `ZEROGRAVITY_APP_ROOT` | Antigravity app root directory |
|
||||
//! | `ZEROGRAVITY_DATA_DIR` | Standalone LS data directory |
|
||||
//! | `ZEROGRAVITY_CONFIG_DIR` | ZeroGravity config directory |
|
||||
//! | `ZEROGRAVITY_LS_USER` | System user for LS isolation (Linux only) |
|
||||
//! | `ZEROGRAVITY_STATE_DB` | Path to Antigravity's state.vscdb |
|
||||
//! | `SSL_CERT_FILE` | System CA certificate bundle |
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// All platform-specific paths, resolved once at startup.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Platform {
|
||||
/// Path to the Language Server binary.
|
||||
pub ls_binary_path: String,
|
||||
/// Antigravity app root (for ANTIGRAVITY_EDITOR_APP_ROOT).
|
||||
pub app_root: String,
|
||||
/// Data directory for standalone LS runtime files.
|
||||
pub data_dir: String,
|
||||
/// Config directory (~/.config/zerogravity or platform equivalent).
|
||||
pub config_dir: PathBuf,
|
||||
/// System CA certificate bundle path.
|
||||
pub ca_cert_path: String,
|
||||
/// System user for UID-scoped isolation (Linux only).
|
||||
pub ls_user: String,
|
||||
/// Path to Antigravity's state.vscdb.
|
||||
pub state_db_path: String,
|
||||
/// Token file path.
|
||||
pub token_path: PathBuf,
|
||||
/// Traces directory.
|
||||
pub traces_dir: PathBuf,
|
||||
/// DNS redirect shared library path (Linux only).
|
||||
pub dns_redirect_so_path: String,
|
||||
/// OS display name for sec-ch-ua-platform header ("Linux", "macOS", "Windows").
|
||||
pub os_name: &'static str,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
/// Detect platform and resolve all paths.
|
||||
///
|
||||
/// Environment variables override platform defaults.
|
||||
pub fn detect() -> Self {
|
||||
let home = home_dir();
|
||||
let config_dir = env_or("ZEROGRAVITY_CONFIG_DIR", || default_config_dir(&home));
|
||||
|
||||
let ls_binary_path = env_or("ZEROGRAVITY_LS_PATH", || default_ls_binary_path());
|
||||
let app_root = env_or("ZEROGRAVITY_APP_ROOT", || default_app_root());
|
||||
let data_dir = env_or("ZEROGRAVITY_DATA_DIR", || default_data_dir());
|
||||
let ca_cert_path = env_or("SSL_CERT_FILE", || default_ca_cert_path());
|
||||
let ls_user = env_or("ZEROGRAVITY_LS_USER", || "zerogravity-ls".into());
|
||||
let state_db_path = env_or("ZEROGRAVITY_STATE_DB", || default_state_db_path(&home));
|
||||
let dns_redirect_so_path = format!("{}/dns-redirect.so", &data_dir);
|
||||
|
||||
let config_dir = PathBuf::from(&config_dir);
|
||||
let token_path = config_dir.join("token");
|
||||
let traces_dir = config_dir.join("traces");
|
||||
|
||||
Self {
|
||||
ls_binary_path,
|
||||
app_root,
|
||||
data_dir,
|
||||
config_dir,
|
||||
ca_cert_path,
|
||||
ls_user,
|
||||
state_db_path,
|
||||
token_path,
|
||||
traces_dir,
|
||||
dns_redirect_so_path,
|
||||
os_name: default_os_name(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn home_dir() -> String {
|
||||
std::env::var("HOME")
|
||||
.or_else(|_| std::env::var("USERPROFILE")) // Windows
|
||||
.unwrap_or_else(|_| "/tmp".into())
|
||||
}
|
||||
|
||||
fn env_or(var: &str, default: impl FnOnce() -> String) -> String {
|
||||
std::env::var(var).unwrap_or_else(|_| default())
|
||||
}
|
||||
|
||||
// ── Platform defaults ──
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn default_ls_binary_path() -> String {
|
||||
"/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn default_ls_binary_path() -> String {
|
||||
let home = home_dir();
|
||||
// Check both /Applications and ~/Applications
|
||||
for base in &[
|
||||
"/Applications/Antigravity.app",
|
||||
&format!("{home}/Applications/Antigravity.app"),
|
||||
] {
|
||||
let path = format!(
|
||||
"{base}/Contents/Resources/app/extensions/antigravity/bin/language_server_darwin_arm64"
|
||||
);
|
||||
if std::path::Path::new(&path).exists() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
"/Applications/Antigravity.app/Contents/Resources/app/extensions/antigravity/bin/language_server_darwin_arm64".into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn default_ls_binary_path() -> String {
|
||||
let local = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| "C:\\Users\\Default\\AppData\\Local".into());
|
||||
format!("{local}\\Programs\\Antigravity\\resources\\app\\extensions\\antigravity\\bin\\language_server_windows_x64.exe")
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
fn default_ls_binary_path() -> String {
|
||||
"language_server".into()
|
||||
}
|
||||
|
||||
// ── App root ──
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn default_app_root() -> String {
|
||||
"/usr/share/antigravity/resources/app".into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn default_app_root() -> String {
|
||||
"/Applications/Antigravity.app/Contents/Resources/app".into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn default_app_root() -> String {
|
||||
let local = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| "C:\\Users\\Default\\AppData\\Local".into());
|
||||
format!("{local}\\Programs\\Antigravity\\resources\\app")
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
fn default_app_root() -> String {
|
||||
".".into()
|
||||
}
|
||||
|
||||
// ── Data dir ──
|
||||
|
||||
fn default_data_dir() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let temp = std::env::var("TEMP").unwrap_or_else(|_| "C:\\Temp".into());
|
||||
format!("{temp}\\zerogravity-standalone")
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
"/tmp/zerogravity-standalone".into()
|
||||
}
|
||||
}
|
||||
|
||||
// ── Config dir ──
|
||||
|
||||
fn default_config_dir(home: &str) -> String {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
format!("{home}/Library/Application Support/zerogravity")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let appdata = std::env::var("APPDATA").unwrap_or_else(|_| format!("{home}\\AppData\\Roaming"));
|
||||
format!("{appdata}\\zerogravity")
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
format!("{home}/.config/zerogravity")
|
||||
}
|
||||
}
|
||||
|
||||
// ── CA certs ──
|
||||
|
||||
fn default_ca_cert_path() -> String {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
"/etc/ssl/cert.pem".into()
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Windows uses native cert store, this is a fallback
|
||||
String::new()
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
// Try common Linux paths
|
||||
for path in &[
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
"/etc/pki/tls/certs/ca-bundle.crt",
|
||||
"/etc/ssl/ca-bundle.pem",
|
||||
] {
|
||||
if std::path::Path::new(path).exists() {
|
||||
return path.to_string();
|
||||
}
|
||||
}
|
||||
"/etc/ssl/certs/ca-certificates.crt".into()
|
||||
}
|
||||
}
|
||||
|
||||
// ── State DB ──
|
||||
|
||||
fn default_state_db_path(home: &str) -> String {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
format!("{home}/Library/Application Support/Antigravity/User/globalStorage/state.vscdb")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let appdata = std::env::var("APPDATA").unwrap_or_else(|_| format!("{home}\\AppData\\Roaming"));
|
||||
format!("{appdata}\\Antigravity\\User\\globalStorage\\state.vscdb")
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
format!("{home}/.config/Antigravity/User/globalStorage/state.vscdb")
|
||||
}
|
||||
}
|
||||
|
||||
// ── OS name ──
|
||||
|
||||
fn default_os_name() -> &'static str {
|
||||
#[cfg(target_os = "linux")]
|
||||
{ "Linux" }
|
||||
#[cfg(target_os = "macos")]
|
||||
{ "macOS" }
|
||||
#[cfg(target_os = "windows")]
|
||||
{ "Windows" }
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
{ "Unknown" }
|
||||
}
|
||||
|
||||
// ── Platform queries ──
|
||||
|
||||
/// Returns true if running on Linux.
|
||||
pub fn is_linux() -> bool {
|
||||
cfg!(target_os = "linux")
|
||||
}
|
||||
|
||||
/// Returns true if running on macOS.
|
||||
#[allow(dead_code)]
|
||||
pub fn is_macos() -> bool {
|
||||
cfg!(target_os = "macos")
|
||||
}
|
||||
|
||||
/// Returns true if running on Windows.
|
||||
#[allow(dead_code)]
|
||||
pub fn is_windows() -> bool {
|
||||
cfg!(target_os = "windows")
|
||||
}
|
||||
|
||||
/// Returns true if UID isolation (iptables + dedicated user) is available.
|
||||
///
|
||||
/// Only supported on Linux with the zerogravity-ls system user.
|
||||
pub fn supports_uid_isolation() -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
std::process::Command::new("id")
|
||||
.args(["-u", "zerogravity-ls"])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if LD_PRELOAD DNS redirect is supported (Linux only).
|
||||
pub fn supports_ld_preload() -> bool {
|
||||
cfg!(target_os = "linux")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_detect_doesnt_panic() {
|
||||
let p = Platform::detect();
|
||||
assert!(!p.ls_binary_path.is_empty());
|
||||
assert!(!p.app_root.is_empty());
|
||||
assert!(!p.data_dir.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_override() {
|
||||
std::env::set_var("ZEROGRAVITY_LS_PATH", "/custom/ls");
|
||||
let p = Platform::detect();
|
||||
assert_eq!(p.ls_binary_path, "/custom/ls");
|
||||
std::env::remove_var("ZEROGRAVITY_LS_PATH");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_dir_has_token_and_traces() {
|
||||
let p = Platform::detect();
|
||||
assert!(p.token_path.ends_with("token"));
|
||||
assert!(p.traces_dir.ends_with("traces"));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! LS process discovery — finding, inspecting, and managing LS processes.
|
||||
|
||||
use super::{MainLSConfig, LS_USER};
|
||||
use super::{paths, MainLSConfig};
|
||||
use crate::platform;
|
||||
use crate::proto::wire::extract_proto_string;
|
||||
use std::net::TcpListener;
|
||||
use std::process::{Command, Stdio};
|
||||
@@ -87,6 +88,8 @@ pub(super) fn find_main_ls_pid() -> Result<String, String> {
|
||||
let target_clean = target_str.trim_end_matches(" (deleted)");
|
||||
// Must be the actual LS binary, not a bash script
|
||||
if target_clean.contains("language_server_linux")
|
||||
|| target_clean.contains("language_server_darwin")
|
||||
|| target_clean.contains("language_server_windows")
|
||||
|| target_clean.contains("antigravity-language-server")
|
||||
{
|
||||
return Ok(name_str.to_string());
|
||||
@@ -107,18 +110,9 @@ pub(super) fn find_free_port() -> Result<u16, String> {
|
||||
.map_err(|e| format!("Failed to get port: {e}"))
|
||||
}
|
||||
|
||||
/// Check if the dedicated LS system user exists.
|
||||
///
|
||||
/// When the user exists, the proxy spawns the LS as that UID so iptables
|
||||
/// can scope the :443 redirect to only the standalone LS process.
|
||||
/// Check if the dedicated LS system user exists (Linux only).
|
||||
pub(super) fn has_ls_user() -> bool {
|
||||
Command::new("id")
|
||||
.args(["-u", LS_USER])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false)
|
||||
platform::supports_uid_isolation()
|
||||
}
|
||||
|
||||
/// Find the PID of a language_server process owned by a specific user.
|
||||
@@ -126,7 +120,7 @@ pub(super) fn has_ls_user() -> bool {
|
||||
/// Used to discover the actual LS process after sudo spawns it as a different user.
|
||||
pub(super) fn find_ls_pid_for_user(user: &str) -> Result<u32, String> {
|
||||
let output = Command::new("pgrep")
|
||||
.args(["-u", user, "-f", "language_server_linux"])
|
||||
.args(["-u", user, "-f", "language_server"])
|
||||
.output()
|
||||
.map_err(|e| format!("pgrep failed: {e}"))?;
|
||||
|
||||
@@ -152,9 +146,11 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all LS processes owned by antigravity-ls user
|
||||
let ls_user = &paths().ls_user;
|
||||
|
||||
// Find all LS processes owned by the LS user
|
||||
let output = Command::new("pgrep")
|
||||
.args(["-u", LS_USER, "-f", "language_server_linux"])
|
||||
.args(["-u", ls_user.as_str(), "-f", "language_server"])
|
||||
.output();
|
||||
|
||||
let pids: Vec<u32> = match output {
|
||||
@@ -180,7 +176,7 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
// and the sudoers rule allows ALL commands as antigravity-ls.
|
||||
for pid in &pids {
|
||||
let ok = Command::new("sudo")
|
||||
.args(["-n", "-u", LS_USER, "kill", "-TERM", &pid.to_string()])
|
||||
.args(["-n", "-u", ls_user.as_str(), "kill", "-TERM", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
@@ -204,7 +200,7 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
|
||||
// Force-kill any survivors
|
||||
let still_alive = Command::new("pgrep")
|
||||
.args(["-u", LS_USER, "-f", "language_server_linux"])
|
||||
.args(["-u", ls_user.as_str(), "-f", "language_server"])
|
||||
.output()
|
||||
.map(|o| !o.stdout.is_empty())
|
||||
.unwrap_or(false);
|
||||
@@ -213,7 +209,7 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
info!("Orphaned LS still alive, force killing");
|
||||
for pid in &pids {
|
||||
let _ = Command::new("sudo")
|
||||
.args(["-n", "-u", LS_USER, "kill", "-KILL", &pid.to_string()])
|
||||
.args(["-n", "-u", ls_user.as_str(), "kill", "-KILL", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
@@ -222,14 +218,14 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
|
||||
// Final check
|
||||
let still_alive = Command::new("pgrep")
|
||||
.args(["-u", LS_USER, "-f", "language_server_linux"])
|
||||
.args(["-u", ls_user.as_str(), "-f", "language_server"])
|
||||
.output()
|
||||
.map(|o| !o.stdout.is_empty())
|
||||
.unwrap_or(false);
|
||||
|
||||
if still_alive {
|
||||
eprintln!("\n \x1b[1;31m⚠ Cannot kill orphaned LS process\x1b[0m");
|
||||
eprintln!(" Run: \x1b[1msudo pkill -u {LS_USER} -f language_server_linux\x1b[0m\n");
|
||||
eprintln!(" Run: \x1b[1msudo pkill -u {} -f language_server\x1b[0m\n", ls_user);
|
||||
}
|
||||
} else {
|
||||
info!("Orphaned LS processes cleaned up");
|
||||
@@ -244,8 +240,7 @@ pub(super) fn cleanup_orphaned_ls() {
|
||||
pub(super) fn read_oauth_from_state_db() -> Option<(String, Vec<u8>)> {
|
||||
use base64::Engine;
|
||||
|
||||
let home = std::env::var("HOME").ok()?;
|
||||
let db_path = format!("{home}/.config/Antigravity/User/globalStorage/state.vscdb");
|
||||
let db_path = paths().state_db_path;
|
||||
|
||||
// Check the DB file exists
|
||||
if !std::path::Path::new(&db_path).exists() {
|
||||
|
||||
@@ -9,6 +9,7 @@ mod discovery;
|
||||
mod spawn;
|
||||
mod stub;
|
||||
|
||||
use crate::platform::{self, Platform};
|
||||
use std::process::Command;
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
@@ -16,25 +17,14 @@ use uuid::Uuid;
|
||||
// Re-export public API
|
||||
pub use spawn::StandaloneLS;
|
||||
|
||||
/// Default path to the LS binary.
|
||||
const LS_BINARY_PATH: &str =
|
||||
"/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64";
|
||||
|
||||
/// App root for ANTIGRAVITY_EDITOR_APP_ROOT env var.
|
||||
const APP_ROOT: &str = "/usr/share/antigravity/resources/app";
|
||||
|
||||
/// Data directory for the standalone LS.
|
||||
const DATA_DIR: &str = "/tmp/zerogravity-standalone";
|
||||
|
||||
/// System user for UID-scoped iptables isolation.
|
||||
const LS_USER: &str = "zerogravity-ls";
|
||||
|
||||
/// Path for the compiled dns_redirect.so preload library.
|
||||
const DNS_REDIRECT_SO_PATH: &str = "/tmp/zerogravity-dns-redirect.so";
|
||||
|
||||
/// Source file for the DNS redirect preload library (relative to binary).
|
||||
/// Source for the DNS redirect preload library (compiled at runtime, Linux only).
|
||||
const DNS_REDIRECT_C_SOURCE: &str = include_str!("../mitm/dns_redirect.c");
|
||||
|
||||
/// Get platform-resolved paths. Convenience accessor.
|
||||
pub(crate) fn paths() -> Platform {
|
||||
Platform::detect()
|
||||
}
|
||||
|
||||
/// Config needed to bootstrap the standalone LS.
|
||||
///
|
||||
/// In normal mode, discovered from the running main LS.
|
||||
@@ -76,14 +66,17 @@ pub fn discover_main_ls_config() -> Result<MainLSConfig, String> {
|
||||
|
||||
/// Build the dns_redirect.so preload library if it doesn't already exist.
|
||||
///
|
||||
/// The library hooks `getaddrinfo()` via LD_PRELOAD to redirect Google API
|
||||
/// domain lookups to 127.0.0.1. This is needed because the LS binary uses
|
||||
/// CGO for DNS resolution (libc getaddrinfo) but raw syscalls for connect(),
|
||||
/// so only DNS can be intercepted via LD_PRELOAD.
|
||||
/// Linux only — hooks `getaddrinfo()` via LD_PRELOAD to redirect Google API
|
||||
/// domain lookups to 127.0.0.1.
|
||||
///
|
||||
/// Returns the path to the .so on success, None on failure.
|
||||
fn build_dns_redirect_so() -> Option<String> {
|
||||
let so_path = DNS_REDIRECT_SO_PATH;
|
||||
if !platform::supports_ld_preload() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let p = paths();
|
||||
let so_path = &p.dns_redirect_so_path;
|
||||
|
||||
// Skip rebuild if already exists
|
||||
if std::path::Path::new(so_path).exists() {
|
||||
@@ -99,13 +92,12 @@ fn build_dns_redirect_so() -> Option<String> {
|
||||
|
||||
// Compile: gcc -shared -fPIC -o dns_redirect.so dns_redirect.c -ldl
|
||||
let output = Command::new("gcc")
|
||||
.args(["-shared", "-fPIC", "-o", so_path, &c_path, "-ldl"])
|
||||
.args(["-shared", "-fPIC", "-o", so_path.as_str(), &c_path, "-ldl"])
|
||||
.output();
|
||||
|
||||
match output {
|
||||
Ok(out) if out.status.success() => {
|
||||
info!("Built dns_redirect.so at {so_path}");
|
||||
// Clean up source
|
||||
let _ = std::fs::remove_file(&c_path);
|
||||
Some(so_path.to_string())
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
//! StandaloneLS — process lifecycle (spawn, wait, kill).
|
||||
|
||||
use super::discovery::{cleanup_orphaned_ls, find_free_port, find_ls_pid_for_user, has_ls_user, read_oauth_from_state_db};
|
||||
use super::discovery::{cleanup_orphaned_ls, find_free_port, find_ls_pid_for_user, read_oauth_from_state_db};
|
||||
use super::stub::stub_handle_connection;
|
||||
use super::{build_dns_redirect_so, MainLSConfig, StandaloneMitmConfig, APP_ROOT, DATA_DIR, LS_BINARY_PATH, LS_USER};
|
||||
use super::{build_dns_redirect_so, paths, MainLSConfig, StandaloneMitmConfig};
|
||||
use crate::platform;
|
||||
use crate::constants;
|
||||
use crate::proto;
|
||||
use std::io::Write;
|
||||
@@ -57,13 +58,16 @@ impl StandaloneLS {
|
||||
1, // DETECT_AND_USE_PROXY_ENABLED
|
||||
);
|
||||
|
||||
let p = paths();
|
||||
let data_dir = &p.data_dir;
|
||||
|
||||
// Setup data dir (mode 1777 so both current user and zerogravity-ls can write)
|
||||
let gemini_dir = format!("{DATA_DIR}/.gemini");
|
||||
let app_data_dir = format!("{DATA_DIR}/.gemini/zerogravity-standalone");
|
||||
let gemini_dir = format!("{data_dir}/.gemini");
|
||||
let app_data_dir = format!("{data_dir}/.gemini/zerogravity-standalone");
|
||||
let annotations_dir = format!("{app_data_dir}/annotations");
|
||||
let brain_dir = format!("{app_data_dir}/brain");
|
||||
for dir in [
|
||||
DATA_DIR,
|
||||
data_dir.as_str(),
|
||||
&gemini_dir,
|
||||
&app_data_dir,
|
||||
&annotations_dir,
|
||||
@@ -83,7 +87,7 @@ impl StandaloneLS {
|
||||
eprintln!(
|
||||
"\n ⚠ Data dir {} is not writable (owned by another user from previous sudo run)\n \
|
||||
Fix with: sudo chmod -R a+rwX {}\n",
|
||||
app_data_dir, DATA_DIR
|
||||
app_data_dir, data_dir
|
||||
);
|
||||
} else {
|
||||
let _ = std::fs::remove_file(&test_path);
|
||||
@@ -142,9 +146,7 @@ impl StandaloneLS {
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| {
|
||||
let home = std::env::var("HOME").unwrap_or_default();
|
||||
let path = format!("{home}/.config/zerogravity/token");
|
||||
std::fs::read_to_string(&path)
|
||||
std::fs::read_to_string(&p.token_path)
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
@@ -234,7 +236,7 @@ impl StandaloneLS {
|
||||
|
||||
// Build env vars for the LS process
|
||||
let mut env_vars: Vec<(String, String)> =
|
||||
vec![("ANTIGRAVITY_EDITOR_APP_ROOT".into(), APP_ROOT.into())];
|
||||
vec![("ANTIGRAVITY_EDITOR_APP_ROOT".into(), p.app_root.clone())];
|
||||
|
||||
// If MITM is enabled, add SSL + proxy env vars
|
||||
if let Some(mitm) = mitm_config {
|
||||
@@ -242,9 +244,9 @@ impl StandaloneLS {
|
||||
// need a combined bundle: system CAs + our MITM CA
|
||||
// Write to /tmp — accessible by zerogravity-ls user
|
||||
// (user's ~/.config/ is not traversable by other UIDs)
|
||||
let combined_ca_path = "/tmp/zerogravity-mitm-ca.pem".to_string();
|
||||
let combined_ca_path = format!("{}/mitm-ca.pem", data_dir);
|
||||
let system_ca =
|
||||
std::fs::read_to_string("/etc/ssl/certs/ca-certificates.crt").unwrap_or_default();
|
||||
std::fs::read_to_string(&p.ca_cert_path).unwrap_or_default();
|
||||
let mitm_ca = std::fs::read_to_string(&mitm.ca_cert_path)
|
||||
.map_err(|e| format!("Failed to read MITM CA cert: {e}"))?;
|
||||
std::fs::write(&combined_ca_path, format!("{system_ca}\n{mitm_ca}"))
|
||||
@@ -271,7 +273,7 @@ impl StandaloneLS {
|
||||
// OR when running in headless mode (no sudo at all).
|
||||
// With iptables, all outbound traffic is transparently redirected at the
|
||||
// kernel level — setting HTTPS_PROXY on top causes double-proxying.
|
||||
if headless || !has_ls_user() {
|
||||
if headless || !platform::supports_uid_isolation() {
|
||||
// proxy_addr already includes the scheme (e.g. "http://127.0.0.1:8742")
|
||||
env_vars.push(("HTTPS_PROXY".into(), mitm.proxy_addr.clone()));
|
||||
env_vars.push(("HTTP_PROXY".into(), mitm.proxy_addr.clone()));
|
||||
@@ -286,7 +288,7 @@ impl StandaloneLS {
|
||||
env_vars.push(("LD_PRELOAD".into(), so));
|
||||
env_vars.push((
|
||||
"DNS_REDIRECT_LOG".into(),
|
||||
"/tmp/zerogravity-dns-redirect.log".into(),
|
||||
format!("{data_dir}/dns-redirect.log"),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -294,21 +296,23 @@ impl StandaloneLS {
|
||||
|
||||
// In headless mode, never use sudo — run as current user
|
||||
// In normal mode, use sudo if 'zerogravity-ls' user exists
|
||||
let use_sudo = !headless && has_ls_user();
|
||||
let use_sudo = !headless && platform::supports_uid_isolation();
|
||||
let ls_binary = &p.ls_binary_path;
|
||||
let ls_user = &p.ls_user;
|
||||
|
||||
let mut cmd = if use_sudo {
|
||||
info!("Using UID isolation: spawning LS as 'zerogravity-ls' user");
|
||||
info!("Using UID isolation: spawning LS as '{}' user", ls_user);
|
||||
let mut c = Command::new("sudo");
|
||||
c.args(["-n", "-u", LS_USER, "--", "/usr/bin/env"]);
|
||||
c.args(["-n", "-u", ls_user.as_str(), "--", "/usr/bin/env"]);
|
||||
for (k, v) in &env_vars {
|
||||
c.arg(format!("{k}={v}"));
|
||||
}
|
||||
c.arg(LS_BINARY_PATH);
|
||||
c.arg(ls_binary.as_str());
|
||||
c.args(&args);
|
||||
c
|
||||
} else {
|
||||
debug!("Spawning LS as current user");
|
||||
let mut c = Command::new(LS_BINARY_PATH);
|
||||
let mut c = Command::new(ls_binary.as_str());
|
||||
c.args(&args);
|
||||
for (k, v) in &env_vars {
|
||||
c.env(k, v);
|
||||
@@ -317,7 +321,7 @@ impl StandaloneLS {
|
||||
};
|
||||
|
||||
// Capture stderr for debugging — logs to /tmp so we can diagnose LS failures
|
||||
let stderr_file = std::fs::File::create("/tmp/zerogravity-ls-debug.log")
|
||||
let stderr_file = std::fs::File::create(format!("{data_dir}/ls-debug.log"))
|
||||
.map_err(|e| format!("Failed to create LS debug log: {e}"))?;
|
||||
cmd.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
@@ -343,7 +347,7 @@ impl StandaloneLS {
|
||||
// Give sudo a moment to spawn the real process
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
// Find the LS process owned by zerogravity-ls user
|
||||
find_ls_pid_for_user(LS_USER).ok()
|
||||
find_ls_pid_for_user(ls_user).ok()
|
||||
} else {
|
||||
Some(child.id())
|
||||
};
|
||||
@@ -423,10 +427,11 @@ impl StandaloneLS {
|
||||
if self.use_sudo {
|
||||
// The child is sudo which already exited. Kill the actual LS.
|
||||
if let Some(pid) = self.ls_pid {
|
||||
info!(pid, "Killing LS process via sudo -u {}", LS_USER);
|
||||
let ls_user = &paths().ls_user;
|
||||
info!(pid, "Killing LS process via sudo -u {}", ls_user);
|
||||
// Run kill AS the zerogravity-ls user (same UID can signal)
|
||||
let ok = std::process::Command::new("sudo")
|
||||
.args(["-n", "-u", LS_USER, "kill", "-TERM", &pid.to_string()])
|
||||
.args(["-n", "-u", ls_user.as_str(), "kill", "-TERM", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
@@ -437,7 +442,7 @@ impl StandaloneLS {
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
// Force kill if still alive
|
||||
let _ = std::process::Command::new("sudo")
|
||||
.args(["-n", "-u", LS_USER, "kill", "-KILL", &pid.to_string()])
|
||||
.args(["-n", "-u", ls_user.as_str(), "kill", "-KILL", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
|
||||
@@ -20,11 +20,7 @@ pub struct TraceCollector {
|
||||
|
||||
impl TraceCollector {
|
||||
pub fn new(enabled: bool) -> Self {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||
let traces_dir = PathBuf::from(home)
|
||||
.join(".config")
|
||||
.join("zerogravity")
|
||||
.join("traces");
|
||||
let traces_dir = crate::platform::Platform::detect().traces_dir;
|
||||
Self {
|
||||
enabled,
|
||||
traces_dir,
|
||||
|
||||
Reference in New Issue
Block a user