From e8e1ce1269487c4ef20d8074bb9ba9034fab62e7 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Mon, 4 May 2026 07:50:36 -0700 Subject: [PATCH 1/3] refactor: deduplicate trackPidForPort variants Extract shared try/catch + file-read pattern from trackPidForPort (async) and trackPidForPortSync into a common trackPidForPortCore helper. Closes #2480 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/pid-tracker.ts | 94 +++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/pid-tracker.ts b/src/pid-tracker.ts index 21839581..3ed16cea 100644 --- a/src/pid-tracker.ts +++ b/src/pid-tracker.ts @@ -328,34 +328,24 @@ function resolvePidFromTcpContent( } /** - * Main function to track a process by its source port + * Core implementation for trackPidForPort and trackPidForPortSync. * - * This reads /proc/net/tcp to find the socket inode, then scans - * all process file descriptors to find the owning process. - * - * @param srcPort - Source port number from the network connection - * @param procPath - Base path to /proc (default: '/proc', useful for testing) - * @returns PidTrackResult with process information - * - * @example - * ```typescript - * const result = await trackPidForPort(45678); - * if (result.pid !== -1) { - * console.log(`Port 45678 is owned by PID ${result.pid}: ${result.cmdline}`); - * } - * ``` + * Reads /proc/net/tcp using the provided file-reader function, then + * delegates to resolvePidFromTcpContent for the parse/lookup logic. + * Centralizes the identical error-handling shape so both the async + * and sync variants stay in sync. */ -export async function trackPidForPort( +function trackPidForPortCore( + readTcpFile: (tcpPath: string) => string, srcPort: number, - procPath = '/proc' -): Promise { + procPath: string +): PidTrackResult { try { - // Read /proc/net/tcp using async operations const tcpPath = path.join(procPath, 'net', 'tcp'); let tcpContent: string; try { - tcpContent = await fsPromises.readFile(tcpPath, 'utf-8'); + tcpContent = readTcpFile(tcpPath); } catch (err) { return { pid: -1, @@ -377,40 +367,58 @@ export async function trackPidForPort( } /** - * Synchronous version of trackPidForPort for use in contexts where async is not available + * Main function to track a process by its source port + * + * This reads /proc/net/tcp to find the socket inode, then scans + * all process file descriptors to find the owning process. * * @param srcPort - Source port number from the network connection - * @param procPath - Base path to /proc (default: '/proc') + * @param procPath - Base path to /proc (default: '/proc', useful for testing) * @returns PidTrackResult with process information + * + * @example + * ```typescript + * const result = await trackPidForPort(45678); + * if (result.pid !== -1) { + * console.log(`Port 45678 is owned by PID ${result.pid}: ${result.cmdline}`); + * } + * ``` */ -export function trackPidForPortSync(srcPort: number, procPath = '/proc'): PidTrackResult { +export async function trackPidForPort( + srcPort: number, + procPath = '/proc' +): Promise { + // Read asynchronously, then delegate to the shared core. + // The core uses a synchronous reader callback, so we pre-read here. try { - // Read /proc/net/tcp const tcpPath = path.join(procPath, 'net', 'tcp'); - let tcpContent: string; - - try { - tcpContent = fs.readFileSync(tcpPath, 'utf-8'); - } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Failed to read ${tcpPath}: ${err}`, - }; - } - + const tcpContent = await fsPromises.readFile(tcpPath, 'utf-8'); return resolvePidFromTcpContent(tcpContent, srcPort, procPath); } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Unexpected error: ${err}`, - }; + // Fall back to the synchronous core for consistent error handling + return trackPidForPortCore( + (p) => fs.readFileSync(p, 'utf-8'), + srcPort, + procPath + ); } } +/** + * Synchronous version of trackPidForPort for use in contexts where async is not available + * + * @param srcPort - Source port number from the network connection + * @param procPath - Base path to /proc (default: '/proc') + * @returns PidTrackResult with process information + */ +export function trackPidForPortSync(srcPort: number, procPath = '/proc'): PidTrackResult { + return trackPidForPortCore( + (p) => fs.readFileSync(p, 'utf-8'), + srcPort, + procPath + ); +} + /** * Checks if PID tracking is available on the current system * (requires /proc filesystem to be mounted and readable) From acd380e7590348e06388ff02dad75a7b25569c46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 15:02:21 +0000 Subject: [PATCH 2/3] fix(pid-tracker): fix unused err and remove trackPidForPortCore --- src/pid-tracker.ts | 82 +++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/src/pid-tracker.ts b/src/pid-tracker.ts index 3ed16cea..da3752df 100644 --- a/src/pid-tracker.ts +++ b/src/pid-tracker.ts @@ -327,45 +327,6 @@ function resolvePidFromTcpContent( }; } -/** - * Core implementation for trackPidForPort and trackPidForPortSync. - * - * Reads /proc/net/tcp using the provided file-reader function, then - * delegates to resolvePidFromTcpContent for the parse/lookup logic. - * Centralizes the identical error-handling shape so both the async - * and sync variants stay in sync. - */ -function trackPidForPortCore( - readTcpFile: (tcpPath: string) => string, - srcPort: number, - procPath: string -): PidTrackResult { - try { - const tcpPath = path.join(procPath, 'net', 'tcp'); - let tcpContent: string; - - try { - tcpContent = readTcpFile(tcpPath); - } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Failed to read ${tcpPath}: ${err}`, - }; - } - - return resolvePidFromTcpContent(tcpContent, srcPort, procPath); - } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Unexpected error: ${err}`, - }; - } -} - /** * Main function to track a process by its source port * @@ -388,20 +349,21 @@ export async function trackPidForPort( srcPort: number, procPath = '/proc' ): Promise { - // Read asynchronously, then delegate to the shared core. - // The core uses a synchronous reader callback, so we pre-read here. + const tcpPath = path.join(procPath, 'net', 'tcp'); + let tcpContent: string; + try { - const tcpPath = path.join(procPath, 'net', 'tcp'); - const tcpContent = await fsPromises.readFile(tcpPath, 'utf-8'); - return resolvePidFromTcpContent(tcpContent, srcPort, procPath); + tcpContent = await fsPromises.readFile(tcpPath, 'utf-8'); } catch (err) { - // Fall back to the synchronous core for consistent error handling - return trackPidForPortCore( - (p) => fs.readFileSync(p, 'utf-8'), - srcPort, - procPath - ); + return { + pid: -1, + cmdline: 'unknown', + comm: 'unknown', + error: `Failed to read ${tcpPath}: ${err}`, + }; } + + return resolvePidFromTcpContent(tcpContent, srcPort, procPath); } /** @@ -412,11 +374,21 @@ export async function trackPidForPort( * @returns PidTrackResult with process information */ export function trackPidForPortSync(srcPort: number, procPath = '/proc'): PidTrackResult { - return trackPidForPortCore( - (p) => fs.readFileSync(p, 'utf-8'), - srcPort, - procPath - ); + const tcpPath = path.join(procPath, 'net', 'tcp'); + let tcpContent: string; + + try { + tcpContent = fs.readFileSync(tcpPath, 'utf-8'); + } catch (err) { + return { + pid: -1, + cmdline: 'unknown', + comm: 'unknown', + error: `Failed to read ${tcpPath}: ${err}`, + }; + } + + return resolvePidFromTcpContent(tcpContent, srcPort, procPath); } /** From e6a965d2599499558e658395c9f5fe14456a27e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 15:23:17 +0000 Subject: [PATCH 3/3] refactor(pid-tracker): extract makeTcpReadError to eliminate duplication Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/3fdd89e7-919a-4d73-9459-4fde40cb3499 --- src/pid-tracker.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/pid-tracker.ts b/src/pid-tracker.ts index da3752df..4758d731 100644 --- a/src/pid-tracker.ts +++ b/src/pid-tracker.ts @@ -279,6 +279,21 @@ export function getProcessInfo( }; } +/** + * Builds the PidTrackResult returned when /proc/net/tcp cannot be read. + * + * Shared by trackPidForPort and trackPidForPortSync so the error shape + * lives in exactly one place. + */ +function makeTcpReadError(tcpPath: string, err: unknown): PidTrackResult { + return { + pid: -1, + cmdline: 'unknown', + comm: 'unknown', + error: `Failed to read ${tcpPath}: ${err}`, + }; +} + /** * Resolves a PidTrackResult from already-read /proc/net/tcp content. * @@ -355,12 +370,7 @@ export async function trackPidForPort( try { tcpContent = await fsPromises.readFile(tcpPath, 'utf-8'); } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Failed to read ${tcpPath}: ${err}`, - }; + return makeTcpReadError(tcpPath, err); } return resolvePidFromTcpContent(tcpContent, srcPort, procPath); @@ -380,12 +390,7 @@ export function trackPidForPortSync(srcPort: number, procPath = '/proc'): PidTra try { tcpContent = fs.readFileSync(tcpPath, 'utf-8'); } catch (err) { - return { - pid: -1, - cmdline: 'unknown', - comm: 'unknown', - error: `Failed to read ${tcpPath}: ${err}`, - }; + return makeTcpReadError(tcpPath, err); } return resolvePidFromTcpContent(tcpContent, srcPort, procPath);