Skip to main content

proc_cli/commands/
freeze.rs

1//! `proc freeze` - Pause processes with SIGSTOP
2//!
3//! Examples:
4//!   proc freeze node              # Freeze all node processes
5//!   proc freeze :3000             # Freeze process on port 3000
6//!   proc freeze :3000,:8080       # Freeze multiple targets
7//!   proc freeze node --yes        # Skip confirmation
8//!   proc freeze node --dry-run    # Show what would be frozen
9
10#[cfg(unix)]
11use crate::core::{apply_filters, parse_targets, resolve_targets_excluding_self};
12use crate::error::{ProcError, Result};
13#[cfg(unix)]
14use crate::ui::Printer;
15use clap::Args;
16
17/// Freeze (pause) process(es) with SIGSTOP
18#[derive(Args, Debug)]
19pub struct FreezeCommand {
20    /// Target(s): process name, PID, or :port (comma-separated for multiple)
21    #[arg(required = true)]
22    pub target: String,
23
24    /// Skip confirmation prompt
25    #[arg(long, short = 'y')]
26    pub yes: bool,
27
28    /// Show what would be frozen without actually freezing
29    #[arg(long)]
30    pub dry_run: bool,
31
32    /// Output as JSON
33    #[arg(long, short = 'j')]
34    pub json: bool,
35
36    /// Show verbose output
37    #[arg(long, short = 'v')]
38    pub verbose: bool,
39
40    /// Filter by directory (defaults to current directory if no path given)
41    #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
42    pub in_dir: Option<String>,
43
44    /// Filter by process name
45    #[arg(long = "by", short = 'b')]
46    pub by_name: Option<String>,
47}
48
49impl FreezeCommand {
50    /// Executes the freeze command, pausing matched processes with SIGSTOP.
51    #[cfg(unix)]
52    pub fn execute(&self) -> Result<()> {
53        use nix::sys::signal::Signal;
54
55        let printer = Printer::from_flags(self.json, self.verbose);
56
57        let targets = parse_targets(&self.target);
58        let (mut processes, not_found) = resolve_targets_excluding_self(&targets);
59
60        if !not_found.is_empty() {
61            printer.warning(&format!("Not found: {}", not_found.join(", ")));
62        }
63
64        // Apply --in and --by filters
65        apply_filters(&mut processes, &self.in_dir, &self.by_name);
66
67        if processes.is_empty() {
68            return Err(ProcError::ProcessNotFound(self.target.clone()));
69        }
70
71        if self.dry_run {
72            printer.print_dry_run("freeze", &processes);
73            return Ok(());
74        }
75
76        if !printer.ask_confirm("freeze", &processes, self.yes)? {
77            return Ok(());
78        }
79
80        let mut succeeded = Vec::new();
81        let mut failed = Vec::new();
82
83        for proc in &processes {
84            match proc.send_signal(Signal::SIGSTOP) {
85                Ok(()) => succeeded.push(proc.clone()),
86                Err(e) => failed.push((proc.clone(), e.to_string())),
87            }
88        }
89
90        printer.print_action_result("freeze", &succeeded, &failed);
91
92        Ok(())
93    }
94
95    /// Windows stub
96    #[cfg(not(unix))]
97    pub fn execute(&self) -> Result<()> {
98        Err(ProcError::NotSupported(
99            "freeze (SIGSTOP) is not supported on Windows".to_string(),
100        ))
101    }
102}