Skip to main content

proc_cli/commands/
thaw.rs

1//! `proc thaw` - Resume frozen processes with SIGCONT
2//!
3//! Examples:
4//!   proc thaw node              # Resume the frozen processes named node
5//!   proc thaw node --cmd        # ... or with node anywhere in their command line
6//!   proc thaw :3000             # Resume process on port 3000
7//!   proc thaw :3000 :8080       # Resume several targets: separate arguments
8//!   proc thaw node --dry-run    # Show what would be resumed
9//!   proc thaw :3000 --yes       # Skip confirmation (a port or PID, not a name)
10//!
11//! Commas still work (`proc thaw :3000,:8080`) but are deprecated.
12
13#[cfg(unix)]
14use crate::core::{
15    apply_to_each, flatten_targets, guard_targets, resolve_targets_excluding_self, Guards,
16    NameMatch, Process,
17};
18use crate::error::{ProcError, Result};
19#[cfg(unix)]
20use crate::ui::Printer;
21use clap::Args;
22
23/// Resume frozen process(es) with SIGCONT
24#[derive(Args, Debug)]
25pub struct ThawCommand {
26    /// Target(s): process name, PID, or :port (separate arguments for several)
27    #[arg(required = true, num_args = 1.., value_parser = crate::core::validate_target_arg)]
28    pub target: Vec<String>,
29
30    /// Skip confirmation prompt
31    #[arg(long, short = 'y')]
32    pub yes: bool,
33
34    /// Show what would be resumed without actually resuming
35    #[arg(long)]
36    pub dry_run: bool,
37
38    /// Output as JSON
39    #[arg(long, short = 'j')]
40    pub json: bool,
41
42    /// Show verbose output
43    #[arg(long, short = 'v')]
44    pub verbose: bool,
45
46    /// Filter by directory; takes the next word as the directory unless it starts with '-'
47    /// (bare --in is the current directory, --in=DIR is explicit)
48    #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
49    pub in_dir: Option<String>,
50
51    /// Filter by process name
52    #[arg(long = "by", short = 'b')]
53    pub by_name: Option<String>,
54
55    /// Act only if the process still has this start time (Unix seconds, `start_time`
56    /// in its JSON): a PID the OS gave to a new process is ruled out. One PID target
57    #[arg(long = "start-time", value_name = "SECONDS")]
58    pub start_time: Option<u64>,
59
60    /// Match name targets and --by as a substring of the process name or full command line (like pkill -f)
61    #[arg(long)]
62    pub cmd: bool,
63}
64
65impl ThawCommand {
66    /// Executes the thaw command, resuming frozen processes with SIGCONT.
67    #[cfg(unix)]
68    pub fn execute(&self) -> Result<()> {
69        use nix::sys::signal::Signal;
70
71        let printer = Printer::from_flags(self.json, self.verbose);
72
73        let targets = flatten_targets(&self.target);
74        let mode = NameMatch::from_flag(self.cmd);
75        let resolved = resolve_targets_excluding_self(&targets, mode);
76
77        if let Some(warning) = resolved.warning() {
78            printer.warning(&warning);
79        }
80        // the JSON results list the targets that matched nothing
81        let printer = printer.with_not_found(&resolved.not_found);
82        let mut processes = resolved.processes;
83
84        // Apply --in and --by: guards that rule out everything found say so
85        let guards = Guards::new(&self.in_dir, &self.by_name, self.start_time);
86        guard_targets(&mut processes, &self.target, &guards, mode)?;
87
88        // Real CPU readings whenever the list is shown or returned (dry run, the
89        // confirmation prompt, or the error for a missing `--yes`); only `--yes`
90        // acts at once, without the wait
91        if self.dry_run || !self.yes {
92            Process::measure_cpu(&mut processes);
93        }
94
95        if processes.is_empty() {
96            return Err(ProcError::ProcessNotFound(self.target.join(" ")));
97        }
98
99        if self.dry_run {
100            printer.print_dry_run("thaw", &processes);
101            return Ok(());
102        }
103
104        if !printer.ask_confirm("thaw", &processes, self.yes)? {
105            return Ok(());
106        }
107
108        let (succeeded, failed) =
109            apply_to_each(&processes, |proc| proc.send_signal(Signal::SIGCONT));
110
111        printer.print_action_result("thaw", &succeeded, &failed)
112    }
113
114    /// Windows stub
115    #[cfg(not(unix))]
116    pub fn execute(&self) -> Result<()> {
117        Err(ProcError::NotSupported(
118            "thaw (SIGCONT) is not supported on Windows".to_string(),
119        ))
120    }
121}