1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-06

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

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

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

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

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。

os.environ["QUERY_STRING"] = query

stdinへの書き込み

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

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

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

ただ普通にstdout=PIPEと書いてもエラーになってしまいました。
stdinの指定方法はこちらのように、stdin=subprocess.PIPEとsubprocessを付けてあげないといけないみたい。
こんな感じ。argsの内容はsubprocess.check_outputと同じく["echo", "test"]みたいな指定の仕方です。

    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]

サンプル

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

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

参考

リファレンス
Pythonのsubprocessで外部プロセスの標準入出力と戻り値を扱う

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?