#!/usr/bin/env python3 import subprocess import select import time import config import logging.config logging.config.dictConfig(config.logging) log = logging.getLogger('progrunner') from flask import Flask, request, session from flask_restful import Api, Resource app = Flask(__name__) api = Api(app) app.config.from_object(config) class Authenticator(Resource): def post(self): log.info("received login request %s", request.json) if request.json and request.json.get("token", None) == config.token: session["authorized"] = True log.info("session: %s", session) return { "authorized": True } else: session["authorized"] = False log.info("session: %s", session) return { "authorized": False } running = [] class Starter(Resource): def post(self): log.info("session: %s", session) if not session.get("authorized", False): return {} log.info("json: %s", request.json) args = [ request.json["program"] ] if "args" in request.json: args += request.json["args"] log.info("args: %s", args) proc = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) procinfo = { "popen": proc, "pid": proc.pid, "started": time.time(), "stdout": [], "stderr": [] } running.append(procinfo) pid = proc.pid log.info("pid: %s", pid) timeout = request.json.get("timeout", 0.0) log.debug("timeout: %s", timeout) if timeout: get_output(procinfo, timeout) try: procinfo["status"] = proc.wait(0.1) for ln in proc.stdout: procinfo["stdout"].append(ln) for ln in proc.stderr: procinfo["stderr"].append(ln) procinfo["finished"] = time.time() except subprocess.TimeoutExpired: pass return { x: procinfo[x] for x in ["pid", "started", "stdout", "stderr", "status", "finished"] if x in procinfo } class Status(Resource): def get(self, pid): for procinfo in running: if "finished" not in procinfo: get_output(procinfo, 1.0) try: procinfo["status"] = procinfo["popen"].wait(0.1) procinfo["finished"] = time.time() except subprocess.TimeoutExpired: pass for procinfo in running: if procinfo["pid"] == pid: return { x: procinfo[x] for x in ["pid", "started", "stdout", "stderr", "status", "finished"] if x in procinfo } def get_output(procinfo, timeout): proc = procinfo["popen"] p = select.poll() p.register(proc.stdout) p.register(proc.stderr) t0 = time.time() t1 = t0 + timeout eof = False while not eof: dt = t1 - time.time() if dt < 0: break ready = p.poll(dt * 1000) log.debug("ready: %s", ready) for fd in ready: if fd[0] == proc.stdout.fileno(): ln = proc.stdout.readline() log.debug("ln: %s", ln) if ln == "": eof = True continue procinfo["stdout"].append(ln) if fd[0] == proc.stderr.fileno(): ln = proc.stderr.readline() log.debug("ln: %s", ln) if ln == "": eof = True continue procinfo["stderr"].append(ln) api.add_resource( Authenticator, "/login" ) api.add_resource( Starter, "/start" ) api.add_resource( Status, "/status/" ) if __name__ == '__main__': app.run()