2016-02-05 05:39:09 +00:00
|
|
|
# (c) 2016 RedHat
|
2014-10-02 17:07:05 +00:00
|
|
|
#
|
2016-02-05 05:39:09 +00:00
|
|
|
# This file is part of Ansible.
|
2014-10-02 17:07:05 +00:00
|
|
|
#
|
|
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
2014-10-15 23:18:12 +00:00
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2016-02-05 05:39:09 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import ansible.constants as C
|
|
|
|
import time
|
|
|
|
import random
|
|
|
|
|
2017-03-23 20:35:05 +00:00
|
|
|
from ansible.module_utils.six import text_type
|
|
|
|
from ansible.module_utils.six.moves import shlex_quote
|
2016-02-05 05:39:09 +00:00
|
|
|
|
|
|
|
_USER_HOME_PATH_RE = re.compile(r'^~[_.A-Za-z0-9][-_.A-Za-z0-9]*$')
|
|
|
|
|
2016-11-17 21:18:29 +00:00
|
|
|
|
2016-02-05 05:39:09 +00:00
|
|
|
class ShellBase(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
2016-04-06 16:19:22 +00:00
|
|
|
self.env = dict()
|
|
|
|
if C.DEFAULT_MODULE_SET_LOCALE:
|
|
|
|
self.env.update(
|
|
|
|
dict(
|
2017-06-02 11:14:11 +00:00
|
|
|
LANG=C.DEFAULT_MODULE_LANG,
|
|
|
|
LC_ALL=C.DEFAULT_MODULE_LANG,
|
|
|
|
LC_MESSAGES=C.DEFAULT_MODULE_LANG,
|
2016-04-06 16:19:22 +00:00
|
|
|
)
|
|
|
|
)
|
2016-02-05 05:39:09 +00:00
|
|
|
|
|
|
|
def env_prefix(self, **kwargs):
|
|
|
|
env = self.env.copy()
|
|
|
|
env.update(kwargs)
|
2017-06-02 11:14:11 +00:00
|
|
|
return ' '.join(['%s=%s' % (k, shlex_quote(text_type(v))) for k, v in env.items()])
|
2016-02-05 05:39:09 +00:00
|
|
|
|
|
|
|
def join_path(self, *args):
|
|
|
|
return os.path.join(*args)
|
|
|
|
|
|
|
|
# some shells (eg, powershell) are snooty about filenames/extensions, this lets the shell plugin have a say
|
2016-02-12 22:10:13 +00:00
|
|
|
def get_remote_filename(self, pathname):
|
|
|
|
base_name = os.path.basename(pathname.strip())
|
2016-02-05 05:39:09 +00:00
|
|
|
return base_name.strip()
|
|
|
|
|
|
|
|
def path_has_trailing_slash(self, path):
|
|
|
|
return path.endswith('/')
|
|
|
|
|
2016-08-06 01:40:28 +00:00
|
|
|
def chmod(self, paths, mode):
|
|
|
|
cmd = ['chmod', mode]
|
|
|
|
cmd.extend(paths)
|
2016-11-17 21:18:29 +00:00
|
|
|
cmd = [shlex_quote(c) for c in cmd]
|
2016-03-29 05:07:14 +00:00
|
|
|
|
2016-03-21 21:17:53 +00:00
|
|
|
return ' '.join(cmd)
|
|
|
|
|
2016-08-06 01:40:28 +00:00
|
|
|
def chown(self, paths, user):
|
|
|
|
cmd = ['chown', user]
|
|
|
|
cmd.extend(paths)
|
2016-11-17 21:18:29 +00:00
|
|
|
cmd = [shlex_quote(c) for c in cmd]
|
2016-03-21 21:17:53 +00:00
|
|
|
|
|
|
|
return ' '.join(cmd)
|
|
|
|
|
2016-08-06 01:40:28 +00:00
|
|
|
def set_user_facl(self, paths, user, mode):
|
2016-03-21 21:17:53 +00:00
|
|
|
"""Only sets acls for users as that's really all we need"""
|
2016-06-17 14:00:21 +00:00
|
|
|
cmd = ['setfacl', '-m', 'u:%s:%s' % (user, mode)]
|
2016-08-06 01:40:28 +00:00
|
|
|
cmd.extend(paths)
|
2016-11-17 21:18:29 +00:00
|
|
|
cmd = [shlex_quote(c) for c in cmd]
|
2016-03-21 21:17:53 +00:00
|
|
|
|
|
|
|
return ' '.join(cmd)
|
2016-02-05 05:39:09 +00:00
|
|
|
|
|
|
|
def remove(self, path, recurse=False):
|
2016-11-17 21:18:29 +00:00
|
|
|
path = shlex_quote(path)
|
2016-02-05 05:39:09 +00:00
|
|
|
cmd = 'rm -f '
|
|
|
|
if recurse:
|
|
|
|
cmd += '-r '
|
|
|
|
return cmd + "%s %s" % (path, self._SHELL_REDIRECT_ALLNULL)
|
|
|
|
|
2016-03-25 15:13:44 +00:00
|
|
|
def exists(self, path):
|
2016-11-17 21:18:29 +00:00
|
|
|
cmd = ['test', '-e', shlex_quote(path)]
|
2016-03-25 15:13:44 +00:00
|
|
|
return ' '.join(cmd)
|
|
|
|
|
2017-01-25 18:09:36 +00:00
|
|
|
def mkdtemp(self, basefile=None, system=False, mode=None, tmpdir=None):
|
2016-02-05 05:39:09 +00:00
|
|
|
if not basefile:
|
|
|
|
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
2016-04-12 04:02:00 +00:00
|
|
|
|
|
|
|
# When system is specified we have to create this in a directory where
|
|
|
|
# other users can read and access the temp directory. This is because
|
|
|
|
# we use system to create tmp dirs for unprivileged users who are
|
|
|
|
# sudo'ing to a second unprivileged user. The only dirctories where
|
|
|
|
# that is standard are the tmp dirs, /tmp and /var/tmp. So we only
|
|
|
|
# allow one of those two locations if system=True. However, users
|
|
|
|
# might want to have some say over which of /tmp or /var/tmp is used
|
|
|
|
# (because /tmp may be a tmpfs and want to conserve RAM or persist the
|
|
|
|
# tmp files beyond a reboot. So we check if the user set REMOTE_TMP
|
|
|
|
# to somewhere in or below /var/tmp and if so use /var/tmp. If
|
|
|
|
# anything else we use /tmp (because /tmp is specified by POSIX nad
|
|
|
|
# /var/tmp is not).
|
2017-01-25 18:09:36 +00:00
|
|
|
|
2016-04-12 03:00:10 +00:00
|
|
|
if system:
|
2016-04-12 04:02:00 +00:00
|
|
|
if C.DEFAULT_REMOTE_TMP.startswith('/var/tmp'):
|
|
|
|
basetmpdir = '/var/tmp'
|
|
|
|
else:
|
|
|
|
basetmpdir = '/tmp'
|
2017-01-25 18:09:36 +00:00
|
|
|
elif tmpdir is None:
|
2016-04-12 04:02:00 +00:00
|
|
|
basetmpdir = C.DEFAULT_REMOTE_TMP
|
2017-01-25 18:09:36 +00:00
|
|
|
else:
|
|
|
|
basetmpdir = tmpdir
|
|
|
|
|
2016-04-12 04:02:00 +00:00
|
|
|
basetmp = self.join_path(basetmpdir, basefile)
|
|
|
|
|
2016-02-05 05:39:09 +00:00
|
|
|
cmd = 'mkdir -p %s echo %s %s' % (self._SHELL_SUB_LEFT, basetmp, self._SHELL_SUB_RIGHT)
|
2016-04-08 15:18:35 +00:00
|
|
|
cmd += ' %s echo %s=%s echo %s %s' % (self._SHELL_AND, basefile, self._SHELL_SUB_LEFT, basetmp, self._SHELL_SUB_RIGHT)
|
2016-02-05 05:39:09 +00:00
|
|
|
|
|
|
|
# change the umask in a subshell to achieve the desired mode
|
|
|
|
# also for directories created with `mkdir -p`
|
|
|
|
if mode:
|
|
|
|
tmp_umask = 0o777 & ~mode
|
|
|
|
cmd = '%s umask %o %s %s %s' % (self._SHELL_GROUP_LEFT, tmp_umask, self._SHELL_AND, cmd, self._SHELL_GROUP_RIGHT)
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
def expand_user(self, user_home_path):
|
|
|
|
''' Return a command to expand tildes in a path
|
|
|
|
|
|
|
|
It can be either "~" or "~username". We use the POSIX definition of
|
|
|
|
a username:
|
|
|
|
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_426
|
|
|
|
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_276
|
|
|
|
'''
|
|
|
|
|
|
|
|
# Check that the user_path to expand is safe
|
|
|
|
if user_home_path != '~':
|
|
|
|
if not _USER_HOME_PATH_RE.match(user_home_path):
|
2016-11-17 21:18:29 +00:00
|
|
|
# shlex_quote will make the shell return the string verbatim
|
|
|
|
user_home_path = shlex_quote(user_home_path)
|
2016-02-05 05:39:09 +00:00
|
|
|
return 'echo %s' % user_home_path
|
|
|
|
|
|
|
|
def build_module_command(self, env_string, shebang, cmd, arg_path=None, rm_tmp=None):
|
|
|
|
# don't quote the cmd if it's an empty string, because this will break pipelining mode
|
|
|
|
if cmd.strip() != '':
|
2016-11-17 21:18:29 +00:00
|
|
|
cmd = shlex_quote(cmd)
|
2016-05-11 20:14:01 +00:00
|
|
|
|
2016-02-12 22:10:13 +00:00
|
|
|
cmd_parts = []
|
|
|
|
if shebang:
|
|
|
|
shebang = shebang.replace("#!", "").strip()
|
|
|
|
else:
|
|
|
|
shebang = ""
|
|
|
|
cmd_parts.extend([env_string.strip(), shebang, cmd])
|
2016-02-05 05:39:09 +00:00
|
|
|
if arg_path is not None:
|
|
|
|
cmd_parts.append(arg_path)
|
|
|
|
new_cmd = " ".join(cmd_parts)
|
|
|
|
if rm_tmp:
|
|
|
|
new_cmd = '%s; rm -rf "%s" %s' % (new_cmd, rm_tmp, self._SHELL_REDIRECT_ALLNULL)
|
|
|
|
return new_cmd
|
2016-05-19 17:33:17 +00:00
|
|
|
|
|
|
|
def append_command(self, cmd, cmd_to_append):
|
|
|
|
"""Append an additional command if supported by the shell"""
|
|
|
|
|
|
|
|
if self._SHELL_AND:
|
|
|
|
cmd += ' %s %s' % (self._SHELL_AND, cmd_to_append)
|
|
|
|
|
|
|
|
return cmd
|
2017-02-17 08:09:56 +00:00
|
|
|
|
|
|
|
def wrap_for_exec(self, cmd):
|
|
|
|
"""wrap script execution with any necessary decoration (eg '&' for quoted powershell script paths)"""
|
|
|
|
return cmd
|