Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新(準備中)

Jan 22, 2021 - コメント - 日記

shellコマンドをJSON-RPC経由で実行する

JSON-RPC経由でシェルコマンドを実行したい事があって、以下のようなRubyスクリプトを書きました。

以下のようなリクエストに対して、

{
    "jsonrpc":"2.0",
    "id": "1",
    "method":"shell",
    "params": [
        { "command": "ls -a" },
        { "command": "pwd" }
    ]
}

次のようなレスポンスを返します。

{
  "jsonrpc": "2.0",
  "result": [
    {
      "command": "ls -a",
      "stdout.json": null,
      "stdout": ".\n..\n.git\n.gitignore\n.ruby-version\nDockerfile\nGemfile\nGemfile.lock\nREADME.md\napp.rb\n",
      "stderr": "",
      "status": {
        "exitstatus": 0,
        "pid": 73595
      }
    },
    {
      "command": "pwd",
      "stdout.json": null,
      "stdout": "/Users/tumf/work/shell-jsonrpc\n",
      "stderr": "",
      "status": {
        "exitstatus": 0,
        "pid": 73596
      }
    }
  ],
  "id": "1"
}

スクリプトは以下のような感じです

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'bundler/setup'
require 'jimson'
require 'json'
require "open3"

class MyHandler
  extend Jimson::Handler 

  def shell *params
    params.collect { |param|
      c = param["command"]
      o, e, s = Open3.capture3(c, stdin_data: param["stdin"])
      json = JSON.parse(o) rescue nil 
      { command: c, "stdout.json": json, stdout: o, stderr: e, status: { exitstatus: s.to_i, pid: s.pid } }
    }
  rescue =>e
    p e
    p e.backtrace
    raise
  end
end

server = Jimson::Server.new(MyHandler.new)
server.start # serve with webrick on http://0.0.0.0:8999/

ソース一式はgithub: https://github.com/tumf/shell-jsonrpc

DockerイメージはDockerHubに公開してあります。

$ docker push tumf/shell-jsonrpc

起動

$ ruby app.rb

デフォルトで8999番ポートをListenするので、curlで試すなら以下のようにしてください。

$ curl http://0.0.0.0:8999 -d '{"jsonrpc":"2.0", "id": "1", "method":"shell", "params": [{ "command": "ls -a" }, {"command": "pwd"}] }' |jq .