Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 40 additions & 40 deletions salt/modules/cmdmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
if salt.utils.platform.is_windows():
import salt.platform.win
from salt.utils.win_functions import escape_argument as _cmd_quote
from salt.utils.win_functions import shlex_split
from salt.utils.win_runas import runas as win_runas

HAS_WIN_RUNAS = True
Expand Down Expand Up @@ -100,25 +101,20 @@ def _check_cb(cb_):
return lambda x: x


def _python_shell_default(python_shell, shell=False):
def _python_shell_default(python_shell, __pub_jid):
"""
Set python_shell default based on the shell parameter and __opts__['cmd_safe']
"""
if shell:
if salt.utils.platform.is_windows():
# On Windows python_shell / subprocess 'shell' parameter must always be
# False as we prepend the shell manually
return False
else:
# Non-Windows requires python_shell to be enabled
return True if python_shell is None else python_shell
else:
try:
if __opts__.get("cmd_safe", True) is False and python_shell is None:
# Override-switch for python_shell
return True
except NameError:
pass
try:
# Default to python_shell=True when run directly from remote execution
# system. Cross-module calls won't have a jid.
if __pub_jid and python_shell is None:
return True
elif __opts__.get("cmd_safe", True) is False and python_shell is None:
# Override-switch for python_shell
return True
Comment on lines +113 to +115
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there any tests for cmd_safe? I did a quick search and did not notice any.

except NameError:
pass
return python_shell


Expand Down Expand Up @@ -305,7 +301,7 @@ def _run(
log_callback=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=False,
env=None,
clean_env=False,
Expand Down Expand Up @@ -973,7 +969,7 @@ def _run_quiet(
stdin=None,
output_encoding=None,
runas=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=False,
env=None,
template=None,
Expand Down Expand Up @@ -1022,7 +1018,7 @@ def _run_all_quiet(
cwd=None,
stdin=None,
runas=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=False,
env=None,
template=None,
Expand Down Expand Up @@ -1078,7 +1074,7 @@ def run(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -1346,7 +1342,7 @@ def run(

salt '*' cmd.run cmd='sed -e s/=/:/g'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
stderr = subprocess.STDOUT if redirect_stderr else subprocess.PIPE
ret = _run(
cmd,
Expand Down Expand Up @@ -1671,7 +1667,7 @@ def run_stdout(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -1866,7 +1862,7 @@ def run_stdout(

salt '*' cmd.run_stdout "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
ret = _run(
cmd,
runas=runas,
Expand Down Expand Up @@ -1905,7 +1901,7 @@ def run_stderr(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -2100,7 +2096,7 @@ def run_stderr(

salt '*' cmd.run_stderr "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
ret = _run(
cmd,
runas=runas,
Expand Down Expand Up @@ -2139,7 +2135,7 @@ def run_all(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -2377,7 +2373,7 @@ def run_all(

salt '*' cmd.run_all "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
stderr = subprocess.STDOUT if redirect_stderr else subprocess.PIPE
ret = _run(
cmd,
Expand Down Expand Up @@ -2421,7 +2417,7 @@ def retcode(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -2602,7 +2598,7 @@ def retcode(

salt '*' cmd.retcode "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
ret = _run(
cmd,
runas=runas,
Expand Down Expand Up @@ -2639,7 +2635,7 @@ def _retcode_quiet(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=False,
env=None,
clean_env=False,
Expand Down Expand Up @@ -2697,7 +2693,7 @@ def script(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
template=None,
Expand Down Expand Up @@ -2903,7 +2899,8 @@ def script(
saltenv = __opts__.get("saltenv", "base")
except NameError:
saltenv = "base"
python_shell = _python_shell_default(python_shell, shell)

python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))

def _cleanup_tempfile(path):
try:
Expand Down Expand Up @@ -2995,10 +2992,13 @@ def _cleanup_tempfile(path):
os.chmod(path, 320)
os.chown(path, __salt__["file.user_to_uid"](runas), -1)

if isinstance(args, (list, tuple)):
new_cmd = [path, *args] if args else [path]
else:
new_cmd = [path, str(args)] if args else [path]
if isinstance(args, str):
if salt.utils.platform.is_windows():
args = shlex_split(args)
else:
args = shlex.split(args)

new_cmd = [path, *args] if args else [path]

ret = {}
try:
Expand Down Expand Up @@ -3050,7 +3050,7 @@ def script_retcode(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
template="jinja",
Expand Down Expand Up @@ -3395,7 +3395,7 @@ def run_chroot(
stdin=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=True,
binds=None,
env=None,
Expand Down Expand Up @@ -4627,7 +4627,7 @@ def run_bg(
cwd=None,
runas=None,
group=None,
shell=None,
shell=DEFAULT_SHELL,
python_shell=None,
env=None,
clean_env=False,
Expand Down Expand Up @@ -4832,7 +4832,7 @@ def run_bg(

salt '*' cmd.run_bg cmd='ls -lR / | sed -e s/=/:/g > /tmp/dontwait'
"""
python_shell = _python_shell_default(python_shell, shell)
python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", ""))
res = _run(
cmd,
stdin=None,
Expand Down
38 changes: 38 additions & 0 deletions salt/utils/win_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,3 +421,41 @@ def squid_to_guid(squid):
guid += squid_match.group(index)[::-1]
guid += "}"
return guid


def shlex_split(string):
"""
Windows version of shlex.split()

Based on winshlex: https://github.com/jdjebi/winshlex/blob/master/winshlex/lex.py
"""
re_cmd_lex = r""""((?:""|\\["\\]|[^"])*)"?()|(\\\\(?=\\*")|\\")|(&&?|\|\|?|\d?>|[<])|([^\s"&|<>]+)|(\s+)|(.)"""
args = []
accu = None # collects pieces of one arg
for qs, qss, esc, pipe, word, white, fail in re.findall(re_cmd_lex, string):
if word:
pass # most frequent
elif esc:
word = esc[1]
elif white or pipe:
if accu is not None:
args.append(accu)
if pipe:
args.append(pipe)
accu = None
continue
elif fail:
raise ValueError("invalid or incomplete shell string")
elif qs:
word = qs.replace('\\"', '"').replace("\\\\", "\\")
if platform == 0:
word = word.replace('""', '"')
else:
word = qss # may be even empty; must be last

accu = (accu or "") + word

if accu is not None:
args.append(accu)

return args
1 change: 1 addition & 0 deletions tests/integration/modules/test_cmdmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ def test_script_cwd_with_space(self):
)
self.assertEqual(ret["stdout"], " ".join(args))

@pytest.mark.skip_if_not_root
@pytest.mark.destructive_test
def test_tty(self):
"""
Expand Down
24 changes: 12 additions & 12 deletions tests/integration/shell/test_enabled.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ class EnabledTest(ModuleCase):
)

@pytest.mark.skip_on_windows(reason="Skip on Windows OS")
def test_shell_default_disabled(self):
@pytest.mark.skip_on_freebsd
def test_shell_default_enabled(self):
"""
ensure that python_shell defaults to False for cmd.run
ensure that python_shell defaults to True for cmd.run
"""
disabled_ret = (
"first\nsecond\nthird\n|\nwc\n-l\n;\nexport\nSALTY_VARIABLE=saltines"
"\n&&\necho\n$SALTY_VARIABLE\n;\necho\nduh\n&>\n/dev/null"
)
disabled_ret = "3\nsaltines" # the result of running self.cmd in a shell
ret = self.run_function("cmd.run", [self.cmd])
self.assertEqual(ret, disabled_ret)

@pytest.mark.skip_on_windows(reason="Skip on Windows OS")
@pytest.mark.skip_on_freebsd
def test_shell_enabled(self):
def test_shell_disabled(self):
Comment on lines 35 to +36
Copy link
Contributor

Choose a reason for hiding this comment

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

Would good be good to have a test for windows to ensure the expected behavior occurs when python_shell=False is passed on windows.

"""
test shell enabled output for cmd.run
test shell disabled output for cmd.run
"""
enabled_ret = "3\nsaltines" # the result of running self.cmd in a shell
ret = self.run_function("cmd.run", [self.cmd], python_shell=True)
self.assertEqual(ret.strip(), enabled_ret)
Comment on lines -38 to -45
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like it would be good to also have test_shell_enabled test in addition to test_shell_disabled instead of replacing it with test_shell_disabled

disabled_ret = (
"first\nsecond\nthird\n|\nwc\n-l\n;\nexport\nSALTY_VARIABLE=saltines"
"\n&&\necho\n$SALTY_VARIABLE\n;\necho\nduh\n&>\n/dev/null"
)
ret = self.run_function("cmd.run", [self.cmd], python_shell=False)
self.assertEqual(ret.strip(), disabled_ret)

@pytest.mark.skip_on_windows(reason="Skip on Windows OS")
@pytest.mark.skip_on_freebsd
Expand Down
16 changes: 10 additions & 6 deletions tests/pytests/functional/modules/cmd/test_script_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,40 @@ def echo_script(state_tree):


@pytest.mark.parametrize(
"command, expected",
"args, expected",
[
("foo bar", "a: foo, b: bar"),
('foo "bar bar"', "a: foo, b: bar bar"),
(["foo", "bar"], "a: foo, b: bar"),
(["foo foo", "bar bar"], "a: foo foo, b: bar bar"),
],
)
def test_echo(modules, echo_script, command, expected):
def test_echo(modules, echo_script, args, expected):
"""
Test argument processing with a batch script
"""
script = "salt://echo-script/test.bat"
result = modules.cmd.script(script, args=command, shell="cmd")
result = modules.cmd.script(script, args=args, shell="cmd")
assert result["stdout"] == expected


@pytest.mark.parametrize(
"command, expected",
"args, expected",
[
("foo bar", "a: foo, b: bar"),
('foo "bar bar"', "a: foo, b: bar bar"),
(["foo", "bar"], "a: foo, b: bar"),
(["foo foo", "bar bar"], "a: foo foo, b: bar bar"),
],
)
def test_echo_runas(modules, account, echo_script, command, expected):
def test_echo_runas(modules, account, echo_script, args, expected):
"""
Test argument processing with a batch script and runas
"""
script = "salt://echo-script/test.bat"
result = modules.cmd.script(
script,
args=command,
args=args,
shell="cmd",
runas=account.username,
password=account.password,
Expand Down
Loading
Loading