Skip to content

Commit

Permalink
fix process close
Browse files Browse the repository at this point in the history
  • Loading branch information
Vittorio Palmisano committed Jan 26, 2025
1 parent 8fced97 commit 0d443d4
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 19 deletions.
45 changes: 35 additions & 10 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import {
stopThrottle,
throttleLauncher,
} from './throttle'
import { logger, registerExitHandler, resolvePackagePath } from './utils'
import {
getProcessChildren,
logger,
registerExitHandler,
resolvePackagePath,
} from './utils'

const log = logger('throttler')

Expand Down Expand Up @@ -48,14 +53,29 @@ async function main(): Promise<void> {
showHelpOrVersion()

const config = loadConfig(process.argv[2])
const abortControllers = new Set<AbortController>()
const pids = new Set<number>()

await startThrottle(config.throttleConfig)

const stop = async (): Promise<void> => {
log.info('Stopping...')
for (const controller of abortControllers) {
controller.abort('stop')
for (const pid of pids) {
const childPids = await getProcessChildren(pid)
log.debug(`Killing process ${pid} and children: ${childPids}`)
try {
process.kill(pid, 'SIGKILL')
} catch (err: unknown) {
log.debug(`Error killing process ${pid}: ${(err as Error).stack}`)
}
for (const childPid of childPids) {
try {
process.kill(childPid, 'SIGKILL')
} catch (err: unknown) {
log.debug(
`Error killing child process ${childPid}: ${(err as Error).stack}`,
)
}
}
}
await stopThrottle()
process.exit(0)
Expand All @@ -67,27 +87,32 @@ async function main(): Promise<void> {
: []
for (const c of commands) {
const { session, command } = c
const shortName = command.split(' ')[0]
const index = getSessionThrottleIndex(session || 0)
const launcher = await throttleLauncher(command, index)
try {
const abort = new AbortController()
const proc = spawn(launcher, {
shell: false,
stdio: ['ignore', 'ignore', 'pipe'],
stdio: ['ignore', 'pipe', 'pipe'],
detached: false,
signal: abort.signal,
})
abortControllers.add(abort)
if (proc.pid) pids.add(proc.pid)
proc.stdout.on('data', data => {
log.info(`[${shortName}][stdout]`, data.toString().trim())
})
proc.stderr.on('data', data => {
log.info('[stderr]', data.toString().trim())
log.info(`[${shortName}][stderr]`, data.toString().trim())
})
proc.on('error', err => {
if (err.message.startsWith('The operation was aborted')) return
log.error(`Error running command "${command}": ${(err as Error).stack}`)
})
proc.once('exit', code => {
log.info(`Command "${command}" exited with code: ${code || 0}`)
abortControllers.delete(abort)
if (proc.pid) pids.delete(proc.pid)
/* fs.promises.unlink(launcher).catch(err => {
log.warn(`Error unlinking "${launcher}": ${(err as Error).stack}`)
}) */
})
} catch (err: unknown) {
log.error(`Error running command "${command}": ${(err as Error).stack}`)
Expand Down
19 changes: 10 additions & 9 deletions src/throttle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,6 @@ export async function throttleLauncher(
const config = throttleConfig[index]
const mark = index + 1
const launcherPath = `/tmp/throttler-launcher-${index}`
const wrapperPath = `/tmp/throttler-launcher-${index}-wrapper`
const group = `throttler${index}`
const filters = `${config.protocol ? `-p ${config.protocol}` : ''}\
${config.skipSourcePorts ? ` -m multiport ! --sports ${config.skipSourcePorts}` : ''}\
Expand All @@ -475,8 +474,8 @@ ${config.filter ? ` ${config.filter}` : ''}`
await fs.promises.writeFile(
launcherPath,
`#!/bin/bash
getent group ${group} || sudo -n addgroup --system ${group}
sudo -n adduser $USER ${group}
getent group ${group} >/dev/null || sudo -n addgroup --system ${group}
sudo -n adduser $USER ${group} --quiet
rule=$(sudo -n iptables -t mangle -L OUTPUT --line-numbers | grep "owner GID match ${group}" | awk '{print $1}')
if [ -n "$rule" ]; then
Expand All @@ -488,13 +487,15 @@ fi
sudo -n iptables -t mangle -L PREROUTING | grep -q "CONNMARK restore" || sudo -n iptables -t mangle -I PREROUTING 1 -j CONNMARK --restore-mark
sudo -n iptables -t mangle -L POSTROUTING | grep -q "CONNMARK save" || sudo -n iptables -t mangle -I POSTROUTING 1 -j CONNMARK --save-mark
cat <<EOF > ${wrapperPath}
#!/bin/bash
exec ${executablePath} $@
EOF
chmod +x ${wrapperPath}
function stop() {
echo "Stopping throttler"
}
trap stop SIGINT SIGTERM
exec sg ${group} -c ${wrapperPath}`,
echo "running: ${executablePath} $@"
exec newgrp ${group} <<EOF
${executablePath} $@
EOF`,
)
await fs.promises.chmod(launcherPath, 0o755)
return launcherPath
Expand Down
27 changes: 27 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export async function getDefaultNetworkInterface(): Promise<string> {
}

export async function checkNetworkInterface(device: string): Promise<void> {
if (device === 'lo') return
await runShellCommand(`ip route | grep -q "dev ${device}"`)
}

Expand Down Expand Up @@ -220,3 +221,29 @@ SIGNALS.forEach(event =>
process.exit(0)
}),
)

export async function getProcessChildren(pid: number): Promise<number[]> {
log.debug(`getProcessChildren pid=${pid}`)
const pids = []
try {
const p = await runShellCommand(`pgrep -P ${pid}`)
for (const pid of p.stdout.trim().split('\n').map(Number)) {
pids.push(pid)
try {
const childPids = await getProcessChildren(pid)
for (const p of childPids) {
pids.push(p)
}
} catch (err) {
log.debug(
`Error getting process ${pid} children: ${(err as Error).message}`,
)
}
}
} catch (err) {
log.debug(
`Error getting process ${pid} children: ${(err as Error).message}`,
)
}
return pids
}

0 comments on commit 0d443d4

Please sign in to comment.