I have a problem with trying to kill a process created via Command. I'm using this ShellRunner class to run scripts via my front end. The cancel method fails to properly kill the process (tree?).
import {
Child,
Command,
SpawnOptions,
TerminatedPayload,
} from "@tauri-apps/plugin-shell"
import { platform } from "@tauri-apps/plugin-os"
let killCmd: string
const killCommands = {
macos: "kill -9 -TERM $1",
windows: `taskkill /F /PID $1`,
linux: "pkill -9 -s $1",
} as const
killCmd = killCommands[platform() as keyof typeof killCommands]
if (!killCmd) {
alert("Unsupported platform")
throw new Error("Unsupported platform")
}
export type ShellRunnerOptions = {
onData?: (data: string) => void
onEnd?: (data: TerminatedPayload) => void
onError?: (data: string) => void
spawnOpts?: SpawnOptions
args?: string[]
}
enum RunnerState {
Idle,
Running,
Completed,
Cancelling,
Cancelled,
}
export class ShellRunner {
outputs: string[] = []
error?: string
state = RunnerState.Idle
terminatedPayload?: TerminatedPayload
private childHandle: Child | undefined
private command: Command<string>
constructor(public script: string, public opts?: ShellRunnerOptions) {
const args = opts?.args ? ["--", ...opts.args] : []
this.command = Command.create(
"exec-sh",
["-c", script, ...args],
opts?.spawnOpts
)
this.command.stdout.on("data", (data) => {
if (!this.childHandle) return
this.outputs.push(data)
opts?.onData?.(data)
})
this.command.stderr.on("data", (data) => {
if (!this.childHandle) return
this.error = data
opts?.onError?.(data)
})
this.command.on("close", (data) => {
if (!this.childHandle) return
this.terminatedPayload = data
this.state = RunnerState.Completed
this.childHandle = undefined
opts?.onEnd?.(data)
})
}
async start() {
if (this.state !== RunnerState.Idle) return
this.state = RunnerState.Running
this.childHandle = await this.command.spawn()
}
async cancel() {
if (this.state !== RunnerState.Running || !this.childHandle) return
this.state = RunnerState.Cancelling
const res = await this.childHandle.kill()
console.log("kill res", res)
this.state = RunnerState.Cancelled
}
}
I also tried manually killing the process with the child's pid via another command but this throws the error sh: line 1: kill: (3192) - No such process
const pid = this.childHandle.pid
if (!pid) throw new Error("No pid")
const cmd = "kill -9 " + pid
console.log("executing kill cmd", cmd)
await new ShellRunner(cmd, {
onData(data) {
console.log("kill data", data)
},
onEnd: (data) => {
this.state = RunnerState.Cancelled
this.childHandle = undefined
},
onError: (data) => {
console.error(data)
},
}).start()