#@tauri-apps/plugin-shell - Unable to kill child process

2 messages · Page 1 of 1 (latest)

rotund spade
#

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()
#

this is my capabilities/default.json:

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "enables the default permissions",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:allow-document-read",
    "fs:allow-document-write",
    "fs:allow-document-meta",
    "fs:allow-document-read-recursive",
    "fs:allow-document-write-recursive",
    "fs:allow-document-meta-recursive",
    "fs:default",
    "dialog:default",
    "core:window:allow-set-min-size",
    {
      "identifier": "shell:allow-spawn",
      "allow": [
        {
          "name": "exec-sh",
          "cmd": "sh",
          "args": true
        }
      ]
    },
    {
      "identifier": "shell:allow-kill",
      "allow": [
        {
          "name": "exec-sh",
          "cmd": "sh",
          "args": true
        }
      ]
    },
    {
      "identifier": "shell:allow-execute",
      "allow": [
        {
          "name": "exec-sh",
          "cmd": "sh",
          "args": true
        }
      ]
    },
    "os:default"
  ]
}