diff --git a/crates/shepherd-host-linux/src/adapter.rs b/crates/shepherd-host-linux/src/adapter.rs index 54c9f61..0b4eed5 100644 --- a/crates/shepherd-host-linux/src/adapter.rs +++ b/crates/shepherd-host-linux/src/adapter.rs @@ -119,9 +119,16 @@ impl HostAdapter for LinuxHost { (argv.clone(), env.clone(), cwd.clone(), None) } EntryKind::Snap { snap_name, command, args, env } => { - // For snap apps, the command defaults to the snap name - let cmd = command.clone().unwrap_or_else(|| snap_name.clone()); - let mut argv = vec![cmd]; + // For snap apps, we need to use 'snap run ' to launch them. + // The command (if specified) is passed as an argument after the snap name, + // followed by any additional args. + let mut argv = vec!["snap".to_string(), "run".to_string(), snap_name.clone()]; + // If a custom command is specified (different from snap_name), add it + if let Some(cmd) = command { + if cmd != snap_name { + argv.push(cmd.clone()); + } + } argv.extend(args.clone()); (argv, env.clone(), None, Some(snap_name.clone())) } @@ -150,7 +157,12 @@ impl HostAdapter for LinuxHost { }; // Get the command name for fallback killing - let command_name = argv.first().cloned().unwrap_or_default(); + // For snap apps, use the snap_name (not "snap") to avoid killing unrelated processes + let command_name = if let Some(ref snap) = snap_name { + snap.clone() + } else { + argv.first().cloned().unwrap_or_default() + }; let proc = ManagedProcess::spawn( &argv, diff --git a/crates/shepherd-host-linux/src/process.rs b/crates/shepherd-host-linux/src/process.rs index 6f7303b..7c8746e 100644 --- a/crates/shepherd-host-linux/src/process.rs +++ b/crates/shepherd-host-linux/src/process.rs @@ -324,8 +324,11 @@ impl ManagedProcess { /// Send SIGTERM to all processes in this session pub fn terminate(&self) -> HostResult<()> { - // First try to kill by command name - this catches snap apps and re-parented processes - kill_by_command(&self.command_name, Signal::SIGTERM); + // For snap apps, we rely on cgroup-based killing in the adapter, not pkill + // Using pkill with broad patterns like "snap" would kill unrelated processes + if self.snap_name.is_none() { + kill_by_command(&self.command_name, Signal::SIGTERM); + } // Also try to kill the process group let pgid = Pid::from_raw(-(self.pgid as i32)); // Negative for process group @@ -356,8 +359,11 @@ impl ManagedProcess { /// Send SIGKILL to all processes in this session pub fn kill(&self) -> HostResult<()> { - // First try to kill by command name - this catches snap apps and re-parented processes - kill_by_command(&self.command_name, Signal::SIGKILL); + // For snap apps, we rely on cgroup-based killing in the adapter, not pkill + // Using pkill with broad patterns like "snap" would kill unrelated processes + if self.snap_name.is_none() { + kill_by_command(&self.command_name, Signal::SIGKILL); + } // Also try to kill the process group let pgid = Pid::from_raw(-(self.pgid as i32));