Python的 - 用户输入通过CGI。 线程和阅读文件(Python - User input

2019-09-21 02:34发布

看看这个岗位的底部,最终运行的代码。

这是一个工作的Python / CGI脚本,可以通过调用另一个脚本,然后通过当地的插座将它发送的命令,获取用户输入的CGI脚本。


原帖:

据我所知,没有任何方式直接发送用户输入一个Python / CGI脚本已发送媒体链接它的头。 像,警告在特定情况下的用户并等待确认。 也没有我能找到任何公布解决这个。

如果我错了,请大家指正。

我现在有一个Python脚本,可以连接到服务器,上传固件,重新启动,重新连接,改变一些配置文件和等。

有时,它会帮助很多用户可以输入发送到脚本,而无需重新启动脚本,并从一开始就执行它。 重新连接在2G网络耗时过长。

我想,它必须能够对用户输入发送到另一个脚本,然后将其发布到一个文件,该文件第一/主脚本都在注视着,直到临危输入。 这也将是不错,如果能够阻止脚本的执行,止损/杀输入命令。 至于停止/ kill命令,在主脚本需要有2个线程。 如果没有,就知道它应该停止脚本,如果一个进程,如正在执行一个大文件的上传,完成上传前。

同时,我认为multipe用户应该能够使用脚本在同一时间。 因此,必须生成一个唯一的ID的所有主要脚本启动时间。

以下是我认为它可以进行:

主要脚本被调用

Global variable with a unique session ID is generated and sent to client.

Thread 1
    pexpect spawns a "tail -F /var/www/cgi/tmp_cmd.log"

Thread 2
    Thread status "Busy"
    Connects to network element
    Does its usual stuff until it reaches a point where the user needs to interact.
    Prints the message to user and waits for Thread 1 with a timeout of x seconds.
    Thread status "Ready"

第二脚本获取通过AJAX用户调用2头(会话ID和输入)

第二个脚本

Session ID and user input is saved to "/var/www/cgi/tmp_cmd.log"
Execution of the input script ends

主要脚本

Thread 1 
    User input recieved.
    Wait for Thread 2 status to become "Ready" or ignore status if command is equals to "kill" ect.
    Send user input (single line) and start Thread 1 from the beginning
Thread 2 
    Thread 2 status "Busy"
    Input recieved and process stops/continues.
    Thread 2 status "Ready"

我做了一个脚本,媒体链接连接,上传文件,并运行命令。 但是,它不能收到用户输入。

我真的可以使用一些很好的帮助,或者有人告诉我如何处理这个。

当然,只要剧本已经完成了,我将它张贴在这里或在引擎收录,并链接到它,其他人使用。 :)


最终代码

从下面的帖子的帮助下,我终于有工作代码。 它可以使用线程,但停止/取消过程似乎是比较容易的方式让我弄清楚。

客户端 - cgi_send.py

#!/usr/bin/python
import sys, cgi, cgitb, socket

cgitb.enable()
TASKS_DIR = "/var/www/cgi-bin/tmp"

def main():
    global TASKS_DIR

    url = cgi.FieldStorage()
    cmd = str(url.getvalue('cmd'))
    sessionId = str(url.getvalue('session'))
    socketLocation = TASKS_DIR + '/%s.socket' % sessionId

    print '<a href="?session='+sessionId+'&cmd=QUIT">End script</a>&nbsp;<a href="?session='+sessionId+'&cmd=CANCEL">Cancel task</a>'
    print '<form action=""><input type="hidden" name="session" id="session" value="'+sessionId+'" /><input type="text" name="cmd" id="cmd" value="" /><input type="submit" value="Fun!" />'

    try:

        sock = socket.socket(socket.AF_UNIX)
        sock.setblocking(0)
        sock.connect(socketLocation)
        sock.send(cmd)
        sock.close()
        print '<br />Command sent: '+ cmd;

    except IOError:
        print '<br /><b>Operation failed.</b><br /> Could not write to socket: '+ socketLocation
        pass

    sock.close()

    sys.exit();

if __name__ == '__main__':
    sys.stdout.write("Content-type:text/html;charset=utf-8\r\n\r\n")
    sys.stdout.write('<!DOCTYPE html>\n<html><head><title>Test</title></head><body>')

    main()

    print '</body></html>'
    sys.exit()

服务器

#!/usr/bin/python
import sys, os, socket, uuid, time, multiprocessing

# Options
TASKS_DIR = "/var/www/cgi-bin/tmp/"

def main():

    sessionId = str(uuid.uuid4())

    print 'Session ID: '+ sessionId
    sys.stdout.write ('<br /><a href="cgi_send.py?cmd=test&session=' + sessionId +'" target="cmd_window">Send test command</a>')
    sys.stdout.flush()

    address = os.path.join(TASKS_DIR, '%s.socket' % sessionId)

    sock = socket.socket(socket.AF_UNIX)
    sock.setblocking(0)
    sock.settimeout(.1)
    sock.bind(address)
    sock.listen(1)

    taskList = [foo_task, foo_task, foo_task]

    try:
        for task in taskList:

            print "<br />Starting new task"
            runningTask = multiprocessing.Process(target=task)
            runningTask.daemon = True # Needed to make KeyboardInterrupt possible when testing in shell
            runningTask.start()

            while runningTask.is_alive():
                conn = None
                try:
                    conn, addr = sock.accept()
                    data = conn.recv(100).strip()

                except socket.timeout:
                    # nothing ready from a client
                    continue

                except socket.error, e:
                    print "<br />Connection Error from client"

                else:
                    print "<br />"+ data
                    sys.stdout.flush()
                    conn.close()

                    if data == "CANCEL":
                        # temp way to cancel our task
                        print "<br />Cancelling current task."
                        runningTask.terminate()

                    elif data == "QUIT":
                        print "<br />Quitting entire process." 
                        runningTask.terminate()
                        taskList[:] = []

                finally:
                    if conn:
                        conn.close()

    except (KeyboardInterrupt, SystemExit):
        print '\nReceived keyboard interrupt, quitting threads.'

    finally:
        sock.close()
        os.remove(address)

def foo_task():
    i = 1
    while 10 >= i:
        print "<br />Wating for work... "+ str(i)
        sys.stdout.flush()
        i = i + 1
        time.sleep(1)


if __name__ == '__main__':
    sys.stdout.write("Content-type:text/html;charset=utf-8\r\n\r\n")
    sys.stdout.write('<!DOCTYPE html>\n<html><head><title>Test</title></head><body>')

    main()

    print '</body></html>'
    sys.exit()

Answer 1:

CGI脚本是一个非常原始的操作。 它的工作原理基本相同:从你的命令shell中运行任何正常的脚本。 一个HTTP请求到Web服务器发出。 服务器启动一个新的进程,并通过标准输入到脚本传递的参数。 在这一点上,它像一个正常的脚本。

脚本不能得到任何更多的输入,除非它通过某种方式寻找输入,让你在假设,一旦头被发送,Web客户端可以不再直接发送更多的输入是正确的,因为请求已在进行中,和响应已在进行中也是如此。

一个线程监视文件是引入控制环路到脚本的一种方式。 另一种方法是打开一个UNIX 套接字基于每个实例的唯一ID的路径。 于是纷纷跟帖坐在插座输入上。 那么,什么你必须做的就是通过ID返回给Web客户端。 和客户端可以使与ID,那么这将知道正确的UNIX套接字路径发送控制命令到第二个脚本调用:即。

/tmp/script-foo/control/<id>.socket

实际上,你可能只需要1个线程。 你的主线程可以简单地遍历检查插座上的信息,并监视当前正在运行的线程或子进程中运行。 它可能是这样的伪代码:

uid = generate_unique_id()
sock = socket.socket(AF_UNIX)
sock.bind('/tmp/script-foo/control/%s.socket' % uid)
# and set other sock options like timeout

taskList = [a,b,c]
for task in taskList:
    runningTask = start task in thread/process
    while runningTask is running:
        if new data on socket, with timeout N ms
            if command == restart:
                kill runningTask
                taskList = [a,b,c]
                break
            else:
                process command

当Web客户端通过AJAX发送一个命令给你的第二个脚本,它可能看起来像这样的伪代码:

jobid = request.get('id')
cmd = request.get('cmd')
sock = socket.socket(socket.AF_UNIX)
sock.connect('/tmp/script-foo/control/%s.socket' % jobid)
sock.sendall(cmd)
sock.close()

更新

基于您的代码更新,这里是我所建议的工作示例:

import sys
import os
import socket
import uuid 
import time 

# Options
TASKS_DIR = "."

def main():

    sessionId = str(uuid.uuid4())

    print 'Session ID: '+ sessionId
    sys.stdout.write ('<br /><a href="cgi_send.py?cmd=test&session=' + sessionId +'" target="_blank">Send test command</a>')
    sys.stdout.flush()

    address = os.path.join(TASKS_DIR, '%s.socket' % sessionId)

    sock = socket.socket(socket.AF_UNIX)
    sock.setblocking(0)
    sock.settimeout(.1)
    sock.bind(address)
    sock.listen(1)


    fakeTasks = [foo_task, foo_task, foo_task]

    try:
        for task in fakeTasks:

            # pretend we started a task
            runningTask = task()
            # runningTask = Thread(target=task) 
            # runningTask.start()

            # while runningTask.is_alive():   
            while runningTask:
                conn = None
                try:
                    conn, addr = sock.accept()
                    data = conn.recv(100).strip()

                except socket.timeout:
                    # nothing ready from a client
                    continue

                except socket.error, e:
                    print "<br />Connection Error from client"

                else:
                    print "<br />"+ data
                    sys.stdout.flush()
                    conn.close()

                    # for the thread version, you will need some 
                    # approach to kill or interrupt it. 
                    # This is just simulating. 
                    if data == "CANCEL":
                        # temp way to cancel our task
                        print "<br />Cancelling current task." 
                        runningTask = False

                    elif data == "QUIT":
                        print "<br />Quitting entire process." 
                        runningTask = False 
                        fakeTasks[:] = []

                finally:
                    if conn:
                        conn.close()

    finally:
        sock.close()
        os.remove(address)



def foo_task():
    print 'foo task'
    return True


if __name__ == '__main__':
    sys.stdout.write("Content-type:text/html;charset=utf-8\r\n\r\n")
    sys.stdout.write('<!DOCTYPE html>\n<html><head><title>Test</title></head><body>')

    main()

    print '</body></html>'
    sys.exit()

相反,使用10秒的全局超时的,可以将其设置为100ms的一样小东西。 它遍历每个任务,并启动它(最终在一个线程),然后试图遍历等待套接字连接。 如果在100ms内没有连接,它就会超时,并继续循环,同时检查如果任务已经完成。 在任何时候,一个客户端可以连接并发出无论是“取消”或“退出”命令。 插座将接受连接,读它,并作出反应。

你可以看看你不要在这里需要多线程的解决方案。 你唯一需要的线程或子进程是运行任务。



文章来源: Python - User input to CGI via. Threading and reading file