From e4be5c34eb71da9a705bc62fdfcfe6d763022747 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Tue, 26 Nov 2024 11:50:52 +0100 Subject: src: add support for direct command execution Update live shell run mode for the new REST API interface. Evaluate the "inline" field to diferentiate between execution of script in /opt/opengnsys/shell/ and a cmd execution. Remove usage of echo argument of the API REST. Update Windows and Linux mode for direct command execution. Set OutputEncoding environment variable to 'utf-8' in Windows to unify the encoding of stdout for the invoked programs. Decode stdout to utf-8-sig to remove potential BOM. While at this, remove strange legacy ;|\n\r terminator. --- src/linux/ogOperations.py | 30 ++++++++++++++++-------------- src/live/ogOperations.py | 45 +++++++++++++++++++++++++-------------------- src/ogRest.py | 16 ++++++---------- src/restRequest.py | 8 ++++---- src/windows/ogOperations.py | 35 +++++++++++++++++++++-------------- 5 files changed, 72 insertions(+), 62 deletions(-) diff --git a/src/linux/ogOperations.py b/src/linux/ogOperations.py index bbce41a..5813cf7 100644 --- a/src/linux/ogOperations.py +++ b/src/linux/ogOperations.py @@ -7,9 +7,9 @@ # (at your option) any later version. import os +import shlex import psutil import subprocess -from subprocess import CalledProcessError from src.log import OgError from src.ogRest import ThreadState @@ -30,20 +30,22 @@ class OgLinuxOperations: def shellrun(self, request, ogRest): cmd = request.getrun() + is_inline = request.get_inline() + + if not is_inline: + raise OgError("Only inline mode is supported on Linux") + try: - result = subprocess.run(cmd, - shell=True, - stdin=subprocess.DEVNULL, - capture_output=True, - text=True, - check=True) - except CalledProcessError as error: - if error.stderr: - return error.stderr - if error.stdout: - return error.stdout - return "{Non zero exit code and empty output}" - return result.stdout + ogRest.proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + shell=True) + (output, error) = ogRest.proc.communicate() + except (OSError, subprocess.SubprocessError) as e: + raise OgError(f'Error when running "shell run" subprocess: {e}') from e + + output = output.decode('utf-8-sig', errors='replace') + return (ogRest.proc.returncode, cmd, output) def session(self, request, ogRest): raise OgError('Function not implemented') diff --git a/src/live/ogOperations.py b/src/live/ogOperations.py index ef18e33..ee83c4a 100644 --- a/src/live/ogOperations.py +++ b/src/live/ogOperations.py @@ -38,8 +38,6 @@ from src.utils.hw_inventory import get_hardware_inventory, legacy_list_hardware_ from src.log import OgError -OG_SHELL = '/bin/bash' - class OgLiveOperations: def __init__(self, config): self._url = config['opengnsys']['url'] @@ -381,32 +379,38 @@ class OgLiveOperations: def shellrun(self, request, ogRest): cmd = request.getrun() - cmds = cmd.split(";|\n\r") + is_inline = request.get_inline() self._restartBrowser(self._url_log) - shell_path = '/opt/opengnsys/shell/' + if is_inline: + cmds = cmd + else: + cmds = shlex.split(cmd) + shell_path = '/opt/opengnsys/shell/' - restricted_mode = False + try: + shell_path_files = os.listdir(shell_path) + except OSError as e: + raise OgError(f'Error accessing {shell_path}: {e}') from e - for file_name in os.listdir(shell_path): - file_path = os.path.join(shell_path, file_name) + for file_name in shell_path_files: + file_path = os.path.join(shell_path, file_name) - if cmds[0] == file_name: - cmds[0] = file_path - restricted_mode = True - break + if cmds[0] == file_name: + cmds[0] = file_path + cmd = " ".join(cmds) + break + else: + raise OgError(f'Script {cmds[0]} not found in {shell_path}') try: - if restricted_mode: - ogRest.proc = subprocess.Popen(cmds, stdout=subprocess.PIPE) - else: - ogRest.proc = subprocess.Popen(cmds, - stdout=subprocess.PIPE, - shell=True, - executable=OG_SHELL) + ogRest.proc = subprocess.Popen( + cmds, + stdout=subprocess.PIPE, + shell=is_inline) (output, error) = ogRest.proc.communicate() - except OSError as e: + except (OSError, subprocess.SubprocessError) as e: raise OgError(f'Error when running "shell run" subprocess: {e}') from e if ogRest.proc.returncode != 0: @@ -416,7 +420,8 @@ class OgLiveOperations: self.refresh(ogRest) - return (ogRest.proc.returncode, " ".join(cmds), output.decode('utf-8')) + output = output.decode('utf-8-sig', errors='replace') + return (ogRest.proc.returncode, cmd, output) def session(self, request, ogRest): disk = request.getDisk() diff --git a/src/ogRest.py b/src/ogRest.py index 5731350..a30c494 100644 --- a/src/ogRest.py +++ b/src/ogRest.py @@ -97,16 +97,12 @@ class ogThread(): ogRest.send_internal_server_error(client, exc=e) return - if request.getEcho(): - json_body = jsonBody() - json_body.add_element('cmd', cmd) - json_body.add_element('out', shellout) - json_body.add_element('retcode', retcode) - response = restResponse(ogResponses.OK, json_body, seq=client.seq) - client.send(response.get()) - else: - response = restResponse(ogResponses.OK, seq=client.seq) - client.send(response.get()) + json_body = jsonBody() + json_body.add_element('cmd', cmd) + json_body.add_element('out', shellout) + json_body.add_element('retcode', retcode) + response = restResponse(ogResponses.OK, json_body, seq=client.seq) + client.send(response.get()) ogRest.state = ThreadState.IDLE diff --git a/src/restRequest.py b/src/restRequest.py index 3098c1a..dc0e656 100644 --- a/src/restRequest.py +++ b/src/restRequest.py @@ -33,7 +33,7 @@ class restRequest: self.type = None self.profile = None self.id = None - self.echo = None + self.inline = None self.code = None self.seq = None self.backup = None @@ -72,7 +72,7 @@ class restRequest: if "run" in json_param: self.run = json_param["run"] try: - self.echo = json_param["echo"] + self.inline = json_param["inline"] except: pass @@ -160,8 +160,8 @@ class restRequest: def getId(self): return self.id - def getEcho(self): - return self.echo + def get_inline(self): + return self.inline def getCode(self): return self.code diff --git a/src/windows/ogOperations.py b/src/windows/ogOperations.py index 23e962c..9a80b0a 100644 --- a/src/windows/ogOperations.py +++ b/src/windows/ogOperations.py @@ -7,10 +7,10 @@ # (at your option) any later version. import os +import locale import ctypes import psutil import subprocess -from subprocess import CalledProcessError import multiprocessing as mp from multiprocessing import Process, freeze_support from src.log import OgError @@ -84,20 +84,27 @@ class OgWindowsOperations: def shellrun(self, request, ogRest): cmd = request.getrun() + is_inline = request.get_inline() + + if not is_inline: + raise OgError("Only inline mode is supported on Windows") + + env = os.environ.copy() + env['OutputEncoding'] = 'utf8' + try: - result = subprocess.run(cmd, - shell=True, - stdin=subprocess.DEVNULL, - capture_output=True, - text=True, - check=True) - except CalledProcessError as error: - if error.stderr: - return error.stderr - if error.stdout: - return error.stdout - return "{Non zero exit code and empty output}" - return (result.returncode, cmd, result.stdout) + ogRest.proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + shell=True, + env=env, + ) + output, error = ogRest.proc.communicate() + except (OSError, subprocess.SubprocessError) as e: + raise OgError(f'Error when running "shell run" subprocess: {e}') from e + + output = output.decode('utf-8-sig', errors='replace') + return (ogRest.proc.returncode, cmd, output) def session(self, request, ogRest): raise OgError('Function not implemented') -- cgit v1.2.3-18-g5258