From 9591cf973161daab5a6b5b9bd7e3db0b6cab8342 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 8 Feb 2021 21:41:46 +0800 Subject: [PATCH] support ports linux --- README.md | 34 +++++++++++++++++++---------- cli.ts | 54 ++++++++++++++++++++++++++++------------------ src/dkill.ts | 17 +++++++++++---- src/port2pid.ts | 53 +++++++++++++++++++++++++++++++++++++++++---- src/tests/utils.ts | 16 ++++++++++++++ version.ts | 2 +- 6 files changed, 135 insertions(+), 41 deletions(-) create mode 100644 src/tests/utils.ts diff --git a/README.md b/README.md index 9a481aa..7d3f9eb 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ ### Run directly ``` -deno run --unstable --allow-run --allow-net https://x.nest.land/dkill@0.2.3/cli.ts +deno run --unstable --allow-run --allow-net https://x.nest.land/dkill@0.3.0/cli.ts ``` ### Install ``` -deno install --unstable --allow-run --allow-net https://x.nest.land/dkill@0.2.3/cli.ts +deno install --unstable --allow-run --allow-net https://x.nest.land/dkill@0.3.0/cli.ts ``` You can then access use it using command `dkill` @@ -34,38 +34,50 @@ Usage: dkill Kill any process by - port: add a semicolon in front to define it as a port. ex: 'dkill :3000' - - pid: a valid integer. ex: 'dkill 12654' + - pid: a valid integer. ex: 'dkill 12654' - process name: not implemented yet Options: -h, --help - Show this help. -V, --version - Show the version number for this program. + -v, --verbose - Increase verbosity + -d, --dryrun - Dry run, List the pids that would have been killed. Does not kill anything + ``` ## Programatic Usage -mod.ts exports multiple functions +mod.ts exports multiple functions that can be used programmatically. Check +source code for info -- dkill -- port2pid -- killPids +- dkill(targets: { pids?: number[]; ports?: number[]; procs?: string[]; }, + opts?: { verbose?: boolean, dryrun?: boolean }) +- port2pid() +- killPids() ## Support - Windows - [x] port - - [x] pids + - [x] pid - [ ] process - Linux - - [ ] port - - [x] pids + - [x] port + - [x] pid - [ ] process - Mac - [ ] port - - [ ] pids + - [ ] pid - [ ] process +## TODOs + +- [ ] provide process names killed +- [ ] kill by process name +- [ ] supply multiple values to cli in on go +- [ ] on linux check if `ss` is present. + ## Inspiration - nodejs [fkill-cli](https://www.npmjs.com/package/fkill-cli) diff --git a/cli.ts b/cli.ts index 11130e1..17014d7 100644 --- a/cli.ts +++ b/cli.ts @@ -12,28 +12,40 @@ await new Command() - process name: not implemented yet`, ) .arguments("") - .option("-v, --verbose", "increase verbosity") - .action(async (opts: { verbose: boolean }, port_proc_pid: string) => { - const ports: number[] = []; - const pids: number[] = []; - const procs: string[] = []; + .option("-v, --verbose", "Increase verbosity") + .option( + "-d, --dryrun", + "Dry run, List the pids that would have been killed. Does not kill anything", + ) + .action( + async ( + opts: { verbose: boolean; dryrun: boolean }, + port_proc_pid: string, + ) => { + const ports: number[] = []; + const pids: number[] = []; + const procs: string[] = []; - // Check if port - if (port_proc_pid.startsWith(":")) { - const port = +port_proc_pid.slice(1); - if (!Number.isInteger(port)) { - console.log(`Invalid port number "port"`); - return; + // Check if port + if (port_proc_pid.startsWith(":")) { + const port = +port_proc_pid.slice(1); + if (!Number.isInteger(port)) { + console.log(`Invalid port number "port"`); + return; + } + ports.push(port); + } else if (Number.isInteger(+port_proc_pid)) { + // check if pid + pids.push(+port_proc_pid); + } else { + // must be a string + procs.push(port_proc_pid); } - ports.push(port); - } else if (Number.isInteger(+port_proc_pid)) { - // check if pid - pids.push(+port_proc_pid); - } else { - // must be a string - procs.push(port_proc_pid); - } - await dkill({ ports, pids, procs }, { verbose: opts.verbose }); - }) + await dkill({ ports, pids, procs }, { + verbose: opts.verbose, + dryrun: opts.dryrun, + }); + }, + ) .parse(Deno.args); diff --git a/src/dkill.ts b/src/dkill.ts index bf40890..9e902d2 100644 --- a/src/dkill.ts +++ b/src/dkill.ts @@ -6,7 +6,7 @@ export async function dkill( ports?: number[]; procs?: string[]; }, - opts?: { verbose?: boolean }, + opts?: { verbose?: boolean; dryrun?: boolean }, ) { let pidsKilled: number[] = []; @@ -18,7 +18,10 @@ export async function dkill( // pids if (pids && pids.length) { - const killed = await KillPids(pids); + let killed = pids; + if (!opts?.dryrun) { + killed = await KillPids(pids); + } pidsKilled = pidsKilled.concat(killed); } @@ -30,7 +33,10 @@ export async function dkill( verbose(`pids for port ${port}: ${pidsOfPort}`); pidsOfAllPorts = pidsOfAllPorts.concat(pidsOfPort); } - const killed = await KillPids(pidsOfAllPorts); + let killed = pidsOfAllPorts; + if (!opts?.dryrun) { + killed = await KillPids(pidsOfAllPorts); + } pidsKilled = pidsKilled.concat(killed); } @@ -41,7 +47,10 @@ export async function dkill( } if (pidsKilled.length) { - console.log("pids Killed", pidsKilled); + console.log( + `${opts?.dryrun ? "list of pids target (not killed):" : "pids killed:"}`, + pidsKilled.join(" "), + ); } else { console.log("No process found"); } diff --git a/src/port2pid.ts b/src/port2pid.ts index a553f77..3400679 100644 --- a/src/port2pid.ts +++ b/src/port2pid.ts @@ -8,6 +8,7 @@ export async function port2pid(port: number): Promise { }); const out = await cmd.output(); + cmd.close(); const outString = new TextDecoder().decode(out); // outstring example // TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 28392 @@ -20,16 +21,60 @@ export async function port2pid(port: number): Promise { const pidColumnsIndex = 4; - cmd.close(); const pids = parsedLines .filter((arr) => arr.length !== 0) // filter last line .map((arr) => +arr[pidColumnsIndex]) // extract pids based on columns - .filter((pid) => Number.isInteger(pid) && pid !== 0); // ensure they are numbers. port 0 can be ignored + .filter((pid) => Number.isInteger(pid) && pid !== 0); // ensure they are numbers. pid 0 can be ignored return [...new Set(pids)]; // remove duplicates; } else if (os === "linux") { - console.log("Platform not supported yet"); - throw Error("Platform not supported yet"); + const cmd = Deno.run({ + // -l: listening + // -p: provide pid + // -n: provide local address + cmd: ["ss", "-lnp"], + stdout: "piped", + stderr: "piped", + }); + + const out = await cmd.output(); + cmd.close(); + const outString = new TextDecoder().decode(out); + + // outstring example + // tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("deno", pid=200, fd=12)) + const parsedLines = outString + .split("\n") + .map((line) => line.match(/\S+/g) || []); + + // parsedLines + // [ [ "LISTEN", "0", "128", "0.0.0.0:8080", "0.0.0.0:*", users:(("deno", pid=200, fd=12)) ], [] ] + + const portColumnIndex = 4; + const pidColumnsIndex = 6; + + // remove first row of titles + parsedLines.shift(); + + const pids = parsedLines + .filter((arr) => arr.length !== 0) // filter last line + .filter((arr) => { + const localAddrArr = arr[portColumnIndex].split(":"); + return localAddrArr.length > 0 ? +localAddrArr[1] === port : false; + }) // filter connection for the targetted port + .map((arr) => { + // arr[pidColumnsIndex] should be like: + // users:(("deno", pid=200, fd=12)) + const strArr = arr[pidColumnsIndex].match(/pid=(.*?),/); + if (!strArr) { + console.log("Line with issues", arr); + throw Error("Invalid parsing"); + } + return +strArr[1]; + }) // extract pids based on columns + .filter((pid) => Number.isInteger(pid) && pid !== 0); // ensure they are numbers. pid 0 can be ignored + + return [...new Set(pids)]; // remove duplicates; } else { console.log("Platform not supported yet"); throw Error("Platform not supported yet"); diff --git a/src/tests/utils.ts b/src/tests/utils.ts new file mode 100644 index 0000000..fb6f2fe --- /dev/null +++ b/src/tests/utils.ts @@ -0,0 +1,16 @@ +/** + * webserver.ts + */ +import { serve } from "https://deno.land/std@0.86.0/http/server.ts"; + +const server = serve({ hostname: "0.0.0.0", port: 8080 }); +console.log(`HTTP webserver running on port :8080`, Deno.build.os); + +for await (const request of server) { + let bodyContent = "Your user-agent is:\n\n"; + bodyContent += request.headers.get("user-agent") || "Unknown"; + + request.respond({ status: 200, body: bodyContent }); +} + +// deno -A ./src/tests/utils diff --git a/version.ts b/version.ts index ed35feb..09f8c8c 100644 --- a/version.ts +++ b/version.ts @@ -1 +1 @@ -export const version = "0.2.3"; +export const version = "0.3.0";