python 環境変数, stdinを制御してコマンドを実行する。

##**はじめに lighttpd cgiのqueryとbodyの扱い**

前回 [python socketサーバーを作成して、マルチスレッドでcgiを利用する](https://qiita.com/developer-kikikaikai/items/a75707a9f9642d4fa230)の続きです。queryとbodyの処理をするの忘れてた(-_-;)
lighttpdcgi仕様をもう少し説明します。

query⇒環境変数QUERY_STRINGに文字列を設定する。
body⇒stdinにデータを書き込む

例えばこんなpython処理で起動時にstdinと環境変数QUERY_STRINGを読み込み、その内容を表示します。

```python:show_query.py
import sys, os

query_string=os.environ.get("QUERY_STRING")
body_data=sys.stdin.read()

if not query_string:
    print('Empty query')
else:
    queries=query_string.split("&")
    for i, query in enumerate(queries):
        print(i,  '=>' , query)

if not body_data:
    print('Empty body')
else:
    print("body => ", str(body_data))
```

するとcgiの実行結果はこんな感じに

```
$ curl -X POST --data-ascii "request_body"  "http://localhost/cgi-bin/show_query.py?test=1"
0 => test=1
body =>  request_body
```

stdinの読み込みは`sys.stdin.read`で、環境変数の読み込みは`os.environ.get("QUERY_STRING")`ですね。これをコマンドサーバー上で実現するには、

1. 環境変数の設定
2. stdinへの書き込み

この2つを実行コマンドに対して行う必要があります。今回はpython内で、実行コマンドへこれらを渡す方法の紹介です。

##**環境変数の設定**

こちらは単純です。python内で実行したコマンド(子プロセス)には環境変数が受け継がれる仕様。
ということで以下のように`os.environ`に設定してあげればOK。

```python:
os.environ["QUERY_STRING"] = query
```

##**stdinへの書き込み**

こちらはエラーが取れず苦労しました。

>子プロセスの標準入力にデータを送りたい場合は、 Popen オブジェクトを stdin=PIPE と指定して作成しなければなりません。同じく、戻り値のタプルから None ではない値を取得するためには、 stdout=PIPE かつ/または stderr=PIPE を指定しなければなりません。

というわけで、`Popen`のPIPEを使ってstdinを実行コマンドと紐づけてあげないといけません。
紐づけさえしてしまえば、PIPEしたstdinに対してwriteすればOK。

ただ普通にstdout=PIPEと書いてもエラーになってしまいました。
stdinの指定方法は[こちら](https://kakurasan.blogspot.com/2017/03/python-run-external-process.html)のように、`stdin=subprocess.PIPE`とsubprocessを付けてあげないといけないみたい。
こんな感じ。`args`の内容は`subprocess.check_output`と同じく["echo", "test"]みたいな指定の仕方です。

```python:
    p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    if len(body) != 0:
        p.stdin.write (body.encode ('ascii'))
    print(args)
    return p.communicate()[0]
```

##**サンプル**

上記を踏まえ、前回のサンプルを更新したので貼っておきます。

```python:CommandService.py
#for socket
import os, sys, socket
#for exec command
import subprocess

class CommandService:

    #constructor, set socket path
    def __init__(self, id):
        self._socket_path="/var/run/command_service_"+ str(id) + ".sock"

    #for server
    def run_server(self):
        if self._is_start():
            print("Already start server")
            return
        else:
            print("Not start server")

        print("Start server")
        waitsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
        try:
            waitsock.bind(self._socket_path)
            while True:
                req_cmd_byte = waitsock.recv(8192)
                if not req_cmd_byte: break
                self._exec_request(req_cmd_byte.decode('utf-8').split(','))
        except socket.error as msg:
            print(msg)
        except:
            print("Execute")
        finally:
            waitsock.close()
            os.unlink(self._socket_path)
            print("Exit server")

    def _is_start(self):
        return os.path.exists(self._socket_path)

    #parse request, call command and send response
    def _exec_request(self, req_cmd):
        command=req_cmd[0]
        query=req_cmd[1]
        body=req_cmd[2]
        sockpath=req_cmd[3]
        print("comamnd:" + command)
        print("query:" + query)
        print("body:" + body)
        print("socket:" + sockpath)

        os.environ["QUERY_STRING"] = query
        callresp =self._call_cmd_with_env(command.split(" ") , body)

        if len(callresp) != 0 :
            self._send(callresp, sockpath)

    def _call_cmd_with_env(self, args, body):
        print("enter")
        try:
            print(args)
            p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
            if len(body) != 0:
                p.stdin.write (body.encode ('ascii'))
            print(args)
            return p.communicate()[0]
        except OSError as msg:
            print(msg)

    #for client
    def send_to_server(self, command, query, body_path, response_sockpath):
        request=command + "," + query + "," + body_path + "," + response_sockpath
        self._send(request.encode('utf-8'), self._socket_path)

    def _send(self, request, sockpath):
        sendsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
        try:
            sendsock.sendto(request, sockpath)
            #sendsock.sendto(b"test, command", self._socket_path)
        except socket.error as msg:
            print(msg)
        finally:
            sendsock.close()
```


## **参考**
[リファレンス](https://docs.python.org/ja/3.6/library/subprocess.html#replacing-functions-from-the-popen2-module)
[Pythonのsubprocessで外部プロセスの標準入出力と戻り値を扱う](https://kakurasan.blogspot.com/2017/03/python-run-external-process.html)