Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sonic-py-common] Add getstatusoutput_noshell() functions to general module #12065

Merged
merged 10 commits into from
Sep 22, 2022
58 changes: 58 additions & 0 deletions src/sonic-py-common/sonic_py_common/general.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys
from subprocess import Popen, STDOUT, PIPE, CalledProcessError, check_output


def load_module_from_source(module_name, file_path):
Expand All @@ -23,3 +24,60 @@ def load_module_from_source(module_name, file_path):
sys.modules[module_name] = module

return module


def getstatusoutput_noshell(cmd):
Copy link
Collaborator

@qiluo-msft qiluo-msft Sep 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getstatusoutput_noshell

Please add unit tests to cover new functions. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, working on UT

"""
This function implements getstatusoutput API from subprocess module
but using shell=False to prevent shell injection.
"""
try:
output = check_output(cmd, text=True, stderr=STDOUT)
exitcode = 0
except CalledProcessError as ex:
output = ex.output
exitcode = ex.returncode
if output[-1:] == '\n':
output = output[:-1]
return exitcode, output


def getstatusoutput_noshell_pipe(cmd1, cmd2):
"""
This function implements getstatusoutput API from subprocess module
but using shell=False to prevent shell injection. Input command includes
pipe command.
"""
cmd = ' '.join(cmd1) + ' | ' + ' '.join(cmd2)
try:
with Popen(cmd1, universal_newlines=True, stdout=PIPE) as p1:
with Popen(cmd2, universal_newlines=True, stdin=p1.stdout, stdout=PIPE) as p2:
output = p2.communicate()[0]
p1.wait()
if p1.returncode != 0 and p2.returncode != 0:
Copy link
Collaborator

@qiluo-msft qiluo-msft Sep 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and

or? #Closed

returncode = p1.returncode if p2.returncode == 0 else p2.returncode
raise CalledProcessError(returncode=returncode, cmd=cmd, output=output)
exitcode = 0
except CalledProcessError as e:
output = e.output
exitcode = e.returncode
if output[-1:] == '\n':
output = output[:-1]
return exitcode, output


def check_output_pipe(cmd1, cmd2):
"""
This function implements check_output API from subprocess module.
Input command includes pipe command.
"""
cmd = ' '.join(cmd1) + ' | ' + ' '.join(cmd2)
with Popen(cmd1, universal_newlines=True, stdout=PIPE) as p1:
with Popen(cmd2, universal_newlines=True, stdin=p1.stdout, stdout=PIPE) as p2:
output = p2.communicate()[0]
p1.wait()
if p1.returncode != 0 and p2.returncode != 0:
Copy link
Collaborator

@qiluo-msft qiluo-msft Sep 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and

and -> or #Closed

returncode = p1.returncode if p2.returncode == 0 else p2.returncode
Copy link
Collaborator

@qiluo-msft qiluo-msft Sep 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1

check p1 return code first, if not zero, throw based on p1 info. otherwise, check p2 return code, and throw based on p2 info. #Closed

raise CalledProcessError(returncode=returncode, cmd=cmd, output=output)
return output