Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions command-signatures/json/pkill.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
{
"name": "pkill",
"description": "Signal processes selected by name and other attributes",
"args": {
"name": "pattern",
"description": "Extended regular expression matched against the process name (or the full command line with -f)",
"generatorName": "process_name"
},
"options": [
{
"name": "--signal",
"description": "Signal to send instead of TERM, as a name or number (procps-ng)",
"args": {
"name": "sig",
"generatorName": "signal_name"
}
},
{
"name": [
"-f",
"--full"
],
"description": "Match against the full command line instead of just the process name"
},
{
"name": [
"-x",
"--exact"
],
"description": "Match only processes whose name exactly equals the pattern"
},
{
"name": [
"-i",
"--ignore-case"
],
"description": "Match case insensitively"
},
{
"name": [
"-n",
"--newest"
],
"description": "Select only the most recently started matching process"
},
{
"name": [
"-o",
"--oldest"
],
"description": "Select only the least recently started matching process"
},
{
"name": [
"-g",
"--pgroup"
],
"description": "Match only processes in the listed process group IDs",
"args": {
"name": "PGID,..."
}
},
{
"name": [
"-G",
"--group"
],
"description": "Match only processes whose real group ID is listed",
"args": {
"name": "GID,..."
}
},
{
"name": [
"-P",
"--parent"
],
"description": "Match only child processes of the listed parents",
"args": {
"name": "PPID,..."
}
},
{
"name": [
"-s",
"--session"
],
"description": "Match only processes in the listed session IDs",
"args": {
"name": "SID,..."
}
},
{
"name": [
"-t",
"--terminal"
],
"description": "Match only processes attached to the listed controlling terminals",
"args": {
"name": "tty,..."
}
},
{
"name": [
"-u",
"--euid"
],
"description": "Match only processes whose effective user ID is listed",
"args": {
"name": "user,...",
"generatorName": "user_name"
}
},
{
"name": [
"-U",
"--uid"
],
"description": "Match only processes whose real user ID is listed",
"args": {
"name": "user,...",
"generatorName": "user_name"
}
},
{
"name": [
"-F",
"--pidfile"
],
"description": "Restrict matches to the PIDs read from the given file",
"args": {
"name": "file",
"template": ["filepaths"]
}
},
{
"name": [
"-L",
"--logpidfile"
],
"description": "Fail if the pidfile given with -F is not locked"
},
{
"name": [
"-c",
"--count"
],
"description": "Print the number of matching processes (procps-ng)"
},
{
"name": [
"-e",
"--echo"
],
"description": "Display the name and PID of each process signalled (procps-ng)"
},
{
"name": [
"-q",
"--queue"
],
"description": "Integer value sent with the signal via sigqueue (procps-ng)",
"args": {
"name": "value"
}
},
{
"name": [
"-H",
"--require-handler"
],
"description": "Match only processes with a handler installed for the signal (procps-ng)"
},
{
"name": [
"-O",
"--older"
],
"description": "Match only processes started more than the given number of seconds ago (procps-ng)",
"args": {
"name": "seconds"
}
},
{
"name": [
"-r",
"--runstates"
],
"description": "Match only processes in the given run states, e.g. D,S,Z (procps-ng)",
"args": {
"name": "D,R,S,Z,..."
}
},
{
"name": [
"-A",
"--ignore-ancestors"
],
"description": "Exclude this process' ancestors from the matches (procps-ng)"
},
{
"name": "--cgroup",
"description": "Match only processes in the listed cgroup v2 names (procps-ng)",
"args": {
"name": "grp,..."
}
},
{
"name": "--ns",
"description": "Match only processes in the same namespaces as the given PID (procps-ng)",
"args": {
"name": "PID"
}
},
{
"name": "--nslist",
"description": "Namespaces considered by --ns: ipc, mnt, net, pid, user, uts (procps-ng)",
"args": {
"name": "ns,..."
}
},
{
"name": "-a",
"description": "Include this process' ancestors in the matches (BSD/macOS)"
},
{
"name": "-I",
"description": "Request confirmation before signalling each process (BSD/macOS)"
},
{
"name": "-S",
"description": "Search for matches among system processes (BSD/macOS)"
},
{
"name": "-v",
"description": "Select processes that do not match the pattern (BSD/macOS)"
},
{
"name": [
"-h",
"--help"
],
"description": "Display help and exit"
},
{
"name": [
"-V",
"--version"
],
"description": "Display version information and exit"
}
]
}
62 changes: 62 additions & 0 deletions command-signatures/src/generators/common.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use lazy_static::lazy_static;
use regex::Regex;
use serde::Deserialize;
use std::collections::{HashMap, HashSet};
use warp_completion_metadata::{
Expand Down Expand Up @@ -160,6 +162,62 @@ pub fn systemd_user_units_generator() -> Generator {
Generator::script(systemd_units_command(true), systemd_units)
}

/// Parses `ps -o comm` output into suggestions naming the running executables.
///
/// macOS reports absolute executable paths where Linux reports bare names, so each
/// line is reduced to its basename, which is what process-name matching expects.
/// A header row is dropped for the `ps` implementations that print one even when
/// the `comm=` format asks for none.
pub fn process_names(output: &str) -> GeneratorResults {
let mut seen = HashSet::new();
output
.lines()
.filter_map(|line| {
let path = line.trim();
if path.is_empty() || path == "COMM" || path == "COMMAND" {
return None;
}
let name = path.rsplit_once('/').map_or(path, |(_, name)| name);
if name.is_empty() || !seen.insert(name.to_string()) {
return None;
}
Some(if name == path {
Suggestion::new(name)
} else {
Suggestion::with_description(name, path)
})
})
.collect_unordered_results()
}

/// Returns a cross-platform generator that lists the names of running processes.
///
/// Shared by the commands that select processes by name, such as `pkill` and `killall`.
pub fn process_names_generator() -> Generator {
Generator::script(
CommandBuilder::pipe(
CommandBuilder::single_command("ps -A -o comm="),
CommandBuilder::single_command("sort -u"),
),
process_names,
)
}

/// Parses `kill -l` output into signal-name suggestions.
pub fn signal_names(output: &str) -> GeneratorResults {
SIGNAL_NAME
.find_iter(output)
.map(|capture| Suggestion::new(capture.as_str()))
.collect_unordered_results()
}

/// Returns a generator that lists the signal names accepted by the shell's `kill`.
///
/// Shared by the commands that take a signal, such as `kill` and `pkill`.
pub fn signal_names_generator() -> Generator {
Generator::script(CommandBuilder::single_command("env kill -l"), signal_names)
}

/// Returns a cross-platform generator that lists local user names.
///
/// Uses `getent passwd` on Linux, `dscl` on macOS, and falls back to `/etc/passwd`.
Expand All @@ -180,3 +238,7 @@ pub fn users_generator() -> Generator {
},
)
}

lazy_static! {
static ref SIGNAL_NAME: Regex = Regex::new(r"(\w+)").unwrap();
}
17 changes: 3 additions & 14 deletions command-signatures/src/generators/kill.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use lazy_static::lazy_static;
use regex::Regex;
use warp_completion_metadata::{
CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResultsCollector, Suggestion,
};

use super::common;

pub fn generator() -> CommandSignatureGenerators {
CommandSignatureGenerators::new("kill")
.add_generator(
Expand All @@ -28,16 +28,5 @@ pub fn generator() -> CommandSignatureGenerators {
},
),
)
.add_generator(
"signal_name",
Generator::script(CommandBuilder::single_command("env kill -l"), |output| {
RE.find_iter(output)
.map(|capture| Suggestion::new(capture.as_str()))
.collect_unordered_results()
}),
)
}

lazy_static! {
static ref RE: Regex = Regex::new(r"(\w+)").unwrap();
.add_generator("signal_name", common::signal_names_generator())
}
29 changes: 2 additions & 27 deletions command-signatures/src/generators/killall.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,9 @@
use warp_completion_metadata::{
CommandBuilder, CommandSignatureGenerators, Generator, GeneratorResultsCollector, Suggestion,
};
use warp_completion_metadata::CommandSignatureGenerators;

use super::common;

pub fn generator() -> CommandSignatureGenerators {
CommandSignatureGenerators::new("killall")
.add_generator("user_name", common::users_generator())
.add_generator(
"process_name",
Generator::script(
CommandBuilder::pipe(
CommandBuilder::single_command("ps -A -o comm"),
CommandBuilder::single_command("sort -u"),
),
|output| {
output
.trim()
.lines()
.filter_map(|path| {
path.rsplit_once('/').and_then(|(_, name)| {
if !name.is_empty() {
Some(Suggestion::with_description(name, path))
} else {
None
}
})
})
.collect_unordered_results()
},
),
)
.add_generator("process_name", common::process_names_generator())
}
Loading
Loading