From e20daf639dd271268b172b42adc0d1b9d2103883 Mon Sep 17 00:00:00 2001 From: Alvaro Neira Ayuso Date: Fri, 27 Dec 2019 13:46:49 +0100 Subject: Add shell run and output commands Opengnsys needs a support to execute commands on the machine. This patch adds the support for executing two new commands "shell/run" and "shell/output". The first one, give us the support for executing a command in the machine and keep save in a queue the output. The second one, give us the support for sending the output from the command executed. --- src/HTTPParser.py | 24 +++++++++++++++------ src/linux/ogOperations.py | 5 +++++ src/ogClient.py | 4 ++-- src/ogRest.py | 54 +++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/HTTPParser.py b/src/HTTPParser.py index 7c64805..47c2204 100644 --- a/src/HTTPParser.py +++ b/src/HTTPParser.py @@ -1,5 +1,6 @@ import email from io import StringIO +import json class HTTPParser: def __init__(self): @@ -11,24 +12,32 @@ class HTTPParser: self.contentLen = None self.operation = None self.URI = None + self.cmd = None def parser(self,data): self.requestLine, self.headersAlone = data.split('\n', 1) self.headers = email.message_from_file(StringIO(self.headersAlone)) - if 'host' in self.headers.keys(): - self.host = self.headers['host'] + if 'Host' in self.headers.keys(): + self.host = self.headers['Host'] - if 'content-type' in self.headers.keys(): - self.contentType = self.headers['content-type'] + if 'Content-Type' in self.headers.keys(): + self.contentType = self.headers['Content-Type'] - if 'content-length' in self.headers.keys(): - self.contentLen = int(self.headers['content-length']) + if 'Content-Length' in self.headers.keys(): + self.contentLen = int(self.headers['Content-Length']) if (not self.requestLine == None or not self.requestLine == ''): self.operation = self.requestLine.split('/', 1)[0] self.URI = self.requestLine.split('/', 1)[1] + if not self.contentLen == 0: + msgs = self.headersAlone.rstrip().split('\n') + cmd = msgs[len(msgs) - 1] + jsoncmd = json.loads(cmd) + if "run" in cmd: + self.cmd = jsoncmd["run"] + def getHeaderLine(self): return self.headersAlone @@ -52,3 +61,6 @@ class HTTPParser: def getURI(self): return self.URI + + def getCMD(self): + return self.cmd diff --git a/src/linux/ogOperations.py b/src/linux/ogOperations.py index e3d69e2..1502f40 100644 --- a/src/linux/ogOperations.py +++ b/src/linux/ogOperations.py @@ -14,3 +14,8 @@ def reboot(): subprocess.call('source ' + OG_SCRIPT_PATH + 'etc/preinit/loadenviron.sh; ' + OG_SCRIPT_PATH + 'scripts/reboot', shell=True) else: subprocess.call(['/sbin/reboot']) + +def execCMD(cmd): + cmds = cmd.split(" ") + result = subprocess.run(cmds, stdout=subprocess.PIPE) + return result.stdout.decode('utf-8') diff --git a/src/ogClient.py b/src/ogClient.py index fb87bac..6aceeaa 100644 --- a/src/ogClient.py +++ b/src/ogClient.py @@ -18,6 +18,7 @@ class ogClient: def __init__(self, ip, port): self.ip = ip self.port = port + self.ogrest = ogRest() def get_socket(self): return self.sock @@ -74,7 +75,6 @@ class ogClient: self.data = self.data + data httpparser = HTTPParser() - ogrest = ogRest() if not self.trailer: if self.data.find("\r\n") > 0: @@ -89,7 +89,7 @@ class ogClient: if self.trailer and len(self.data) >= self.content_len: httpparser.parser(self.data) - ogrest.processOperation(httpparser.getRequestOP(), httpparser.getURI(), self) + self.ogrest.processOperation(httpparser.getRequestOP(), httpparser.getURI(), httpparser.getCMD(), self) # Cleanup state information from request self.data = "" diff --git a/src/ogRest.py b/src/ogRest.py index 85e961a..ac02a4f 100644 --- a/src/ogRest.py +++ b/src/ogRest.py @@ -2,6 +2,8 @@ import threading import platform import time from enum import Enum +import json +import queue if platform.system() == 'Linux': from src.linux import ogOperations @@ -12,21 +14,44 @@ class ogResponses(Enum): OK=2 class ogRest(): - def getResponse(self, response): + def __init__(self): + self.msgqueue = queue.Queue(1000) + + def buildJsonResponse(self, idstr, content): + data = { idstr :content } + return json.dumps(data) + + def getResponse(self, response, idstr=None, content=None): + msg = '' if response == ogResponses.BAD_REQUEST: - return 'HTTP/1.0 400 Bad request\r\n\r\n' - if response == ogResponses.IN_PROGRESS: - return 'HTTP/1.0 202 Accepted\r\n\r\n' - if response == ogResponses.OK: - return 'HTTP/1.0 200 OK\r\n\r\n' + msg = 'HTTP/1.0 400 Bad request' + elif response == ogResponses.IN_PROGRESS: + msg = 'HTTP/1.0 202 Accepted' + elif response == ogResponses.OK: + msg = 'HTTP/1.0 200 OK' + else: + return msg - def processOperation(self, op, URI, client): + if not content == None: + jsonmsg = self.buildJsonResponse(idstr, content) + msg = msg + '\nContent-Type:application/json' + msg = msg + '\nContent-Length:' + str(len(jsonmsg)) + msg = msg + '\n' + jsonmsg + + msg = msg + '\r\n\r\n' + return msg + + def processOperation(self, op, URI, cmd, client): if ("poweroff" in URI): self.process_poweroff(client) elif ("reboot" in URI): self.process_reboot(client) elif ("probe" in URI): self.process_probe(client) + elif ("shell/run" in URI): + self.process_shellrun(client, cmd) + elif ("shell/output" in URI): + self.process_shellout(client) else: client.send(self.getResponse(ogResponses.BAD_REQUEST)) @@ -53,3 +78,18 @@ class ogRest(): def process_probe(self, client): client.send(self.getResponse(ogResponses.OK)) + + def process_shellrun(self, client, cmd): + if cmd == None: + client.send(self.getResponse(ogResponses.BAD_REQUEST)) + return + + self.msgqueue.put(ogOperations.execCMD(cmd)) + client.send(self.getResponse(ogResponses.IN_PROGRESS)) + + def process_shellout(self, client): + if self.msgqueue.empty(): + client.send(self.getResponse(ogResponses.IN_PROGRESS, 'out', '')) + else: + out = self.msgqueue.get() + client.send(self.getResponse(ogResponses.IN_PROGRESS, 'out', out)) -- cgit v1.2.3-18-g5258