1# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
2# Copyright (c), Toshio Kuratomi <tkuratomi@ansible.com> 2016
3# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
4
5from __future__ import absolute_import, division, print_function
6__metaclass__ = type
7
8FILE_ATTRIBUTES = {
9    'A': 'noatime',
10    'a': 'append',
11    'c': 'compressed',
12    'C': 'nocow',
13    'd': 'nodump',
14    'D': 'dirsync',
15    'e': 'extents',
16    'E': 'encrypted',
17    'h': 'blocksize',
18    'i': 'immutable',
19    'I': 'indexed',
20    'j': 'journalled',
21    'N': 'inline',
22    's': 'zero',
23    'S': 'synchronous',
24    't': 'notail',
25    'T': 'blockroot',
26    'u': 'undelete',
27    'X': 'compressedraw',
28    'Z': 'compresseddirty',
29}
30
31# Ansible modules can be written in any language.
32# The functions available here can be used to do many common tasks,
33# to simplify development of Python modules.
34
35import __main__
36import atexit
37import errno
38import datetime
39import grp
40import fcntl
41import locale
42import os
43import pwd
44import platform
45import re
46import select
47import shlex
48import shutil
49import signal
50import stat
51import subprocess
52import sys
53import tempfile
54import time
55import traceback
56import types
57
58from itertools import chain, repeat
59
60try:
61    import syslog
62    HAS_SYSLOG = True
63except ImportError:
64    HAS_SYSLOG = False
65
66try:
67    from systemd import journal
68    # Makes sure that systemd.journal has method sendv()
69    # Double check that journal has method sendv (some packages don't)
70    has_journal = hasattr(journal, 'sendv')
71except ImportError:
72    has_journal = False
73
74HAVE_SELINUX = False
75try:
76    import ansible.module_utils.compat.selinux as selinux
77    HAVE_SELINUX = True
78except ImportError:
79    pass
80
81# Python2 & 3 way to get NoneType
82NoneType = type(None)
83
84from ansible.module_utils.compat import selectors
85
86from ._text import to_native, to_bytes, to_text
87from ansible.module_utils.common.text.converters import (
88    jsonify,
89    container_to_bytes as json_dict_unicode_to_bytes,
90    container_to_text as json_dict_bytes_to_unicode,
91)
92
93from ansible.module_utils.common.arg_spec import ModuleArgumentSpecValidator
94
95from ansible.module_utils.common.text.formatters import (
96    lenient_lowercase,
97    bytes_to_human,
98    human_to_bytes,
99    SIZE_RANGES,
100)
101
102try:
103    from ansible.module_utils.common._json_compat import json
104except ImportError as e:
105    print('\n{{"msg": "Error: ansible requires the stdlib json: {0}", "failed": true}}'.format(to_native(e)))
106    sys.exit(1)
107
108
109AVAILABLE_HASH_ALGORITHMS = dict()
110try:
111    import hashlib
112
113    # python 2.7.9+ and 2.7.0+
114    for attribute in ('available_algorithms', 'algorithms'):
115        algorithms = getattr(hashlib, attribute, None)
116        if algorithms:
117            break
118    if algorithms is None:
119        # python 2.5+
120        algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
121    for algorithm in algorithms:
122        AVAILABLE_HASH_ALGORITHMS[algorithm] = getattr(hashlib, algorithm)
123
124    # we may have been able to import md5 but it could still not be available
125    try:
126        hashlib.md5()
127    except ValueError:
128        AVAILABLE_HASH_ALGORITHMS.pop('md5', None)
129except Exception:
130    import sha
131    AVAILABLE_HASH_ALGORITHMS = {'sha1': sha.sha}
132    try:
133        import md5
134        AVAILABLE_HASH_ALGORITHMS['md5'] = md5.md5
135    except Exception:
136        pass
137
138from ansible.module_utils.common._collections_compat import (
139    KeysView,
140    Mapping, MutableMapping,
141    Sequence, MutableSequence,
142    Set, MutableSet,
143)
144from ansible.module_utils.common.process import get_bin_path
145from ansible.module_utils.common.file import (
146    _PERM_BITS as PERM_BITS,
147    _EXEC_PERM_BITS as EXEC_PERM_BITS,
148    _DEFAULT_PERM as DEFAULT_PERM,
149    is_executable,
150    format_attributes,
151    get_flags_from_attributes,
152)
153from ansible.module_utils.common.sys_info import (
154    get_distribution,
155    get_distribution_version,
156    get_platform_subclass,
157)
158from ansible.module_utils.pycompat24 import get_exception, literal_eval
159from ansible.module_utils.common.parameters import (
160    env_fallback,
161    remove_values,
162    sanitize_keys,
163    DEFAULT_TYPE_VALIDATORS,
164    PASS_VARS,
165    PASS_BOOLS,
166)
167
168from ansible.module_utils.errors import AnsibleFallbackNotFound, AnsibleValidationErrorMultiple, UnsupportedError
169from ansible.module_utils.six import (
170    PY2,
171    PY3,
172    b,
173    binary_type,
174    integer_types,
175    iteritems,
176    string_types,
177    text_type,
178)
179from ansible.module_utils.six.moves import map, reduce, shlex_quote
180from ansible.module_utils.common.validation import (
181    check_missing_parameters,
182    safe_eval,
183)
184from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses
185from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean
186from ansible.module_utils.common.warnings import (
187    deprecate,
188    get_deprecation_messages,
189    get_warning_messages,
190    warn,
191)
192
193# Note: When getting Sequence from collections, it matches with strings. If
194# this matters, make sure to check for strings before checking for sequencetype
195SEQUENCETYPE = frozenset, KeysView, Sequence
196
197PASSWORD_MATCH = re.compile(r'^(?:.+[-_\s])?pass(?:[-_\s]?(?:word|phrase|wrd|wd)?)(?:[-_\s].+)?$', re.I)
198
199imap = map
200
201try:
202    # Python 2
203    unicode
204except NameError:
205    # Python 3
206    unicode = text_type
207
208try:
209    # Python 2
210    basestring
211except NameError:
212    # Python 3
213    basestring = string_types
214
215_literal_eval = literal_eval
216
217# End of deprecated names
218
219# Internal global holding passed in params.  This is consulted in case
220# multiple AnsibleModules are created.  Otherwise each AnsibleModule would
221# attempt to read from stdin.  Other code should not use this directly as it
222# is an internal implementation detail
223_ANSIBLE_ARGS = None
224
225
226FILE_COMMON_ARGUMENTS = dict(
227    # These are things we want. About setting metadata (mode, ownership, permissions in general) on
228    # created files (these are used by set_fs_attributes_if_different and included in
229    # load_file_common_arguments)
230    mode=dict(type='raw'),
231    owner=dict(type='str'),
232    group=dict(type='str'),
233    seuser=dict(type='str'),
234    serole=dict(type='str'),
235    selevel=dict(type='str'),
236    setype=dict(type='str'),
237    attributes=dict(type='str', aliases=['attr']),
238    unsafe_writes=dict(type='bool', default=False, fallback=(env_fallback, ['ANSIBLE_UNSAFE_WRITES'])),  # should be available to any module using atomic_move
239)
240
241PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?')
242
243# Used for parsing symbolic file perms
244MODE_OPERATOR_RE = re.compile(r'[+=-]')
245USERS_RE = re.compile(r'[^ugo]')
246PERMS_RE = re.compile(r'[^rwxXstugo]')
247
248# Used for determining if the system is running a new enough python version
249# and should only restrict on our documented minimum versions
250_PY3_MIN = sys.version_info[:2] >= (3, 5)
251_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
252_PY_MIN = _PY3_MIN or _PY2_MIN
253if not _PY_MIN:
254    print(
255        '\n{"failed": true, '
256        '"msg": "Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s"}' % ''.join(sys.version.splitlines())
257    )
258    sys.exit(1)
259
260
261#
262# Deprecated functions
263#
264
265def get_platform():
266    '''
267    **Deprecated** Use :py:func:`platform.system` directly.
268
269    :returns: Name of the platform the module is running on in a native string
270
271    Returns a native string that labels the platform ("Linux", "Solaris", etc). Currently, this is
272    the result of calling :py:func:`platform.system`.
273    '''
274    return platform.system()
275
276# End deprecated functions
277
278
279#
280# Compat shims
281#
282
283def load_platform_subclass(cls, *args, **kwargs):
284    """**Deprecated**: Use ansible.module_utils.common.sys_info.get_platform_subclass instead"""
285    platform_cls = get_platform_subclass(cls)
286    return super(cls, platform_cls).__new__(platform_cls)
287
288
289def get_all_subclasses(cls):
290    """**Deprecated**: Use ansible.module_utils.common._utils.get_all_subclasses instead"""
291    return list(_get_all_subclasses(cls))
292
293
294# End compat shims
295
296
297def heuristic_log_sanitize(data, no_log_values=None):
298    ''' Remove strings that look like passwords from log messages '''
299    # Currently filters:
300    # user:pass@foo/whatever and http://username:pass@wherever/foo
301    # This code has false positives and consumes parts of logs that are
302    # not passwds
303
304    # begin: start of a passwd containing string
305    # end: end of a passwd containing string
306    # sep: char between user and passwd
307    # prev_begin: where in the overall string to start a search for
308    #   a passwd
309    # sep_search_end: where in the string to end a search for the sep
310    data = to_native(data)
311
312    output = []
313    begin = len(data)
314    prev_begin = begin
315    sep = 1
316    while sep:
317        # Find the potential end of a passwd
318        try:
319            end = data.rindex('@', 0, begin)
320        except ValueError:
321            # No passwd in the rest of the data
322            output.insert(0, data[0:begin])
323            break
324
325        # Search for the beginning of a passwd
326        sep = None
327        sep_search_end = end
328        while not sep:
329            # URL-style username+password
330            try:
331                begin = data.rindex('://', 0, sep_search_end)
332            except ValueError:
333                # No url style in the data, check for ssh style in the
334                # rest of the string
335                begin = 0
336            # Search for separator
337            try:
338                sep = data.index(':', begin + 3, end)
339            except ValueError:
340                # No separator; choices:
341                if begin == 0:
342                    # Searched the whole string so there's no password
343                    # here.  Return the remaining data
344                    output.insert(0, data[0:begin])
345                    break
346                # Search for a different beginning of the password field.
347                sep_search_end = begin
348                continue
349        if sep:
350            # Password was found; remove it.
351            output.insert(0, data[end:prev_begin])
352            output.insert(0, '********')
353            output.insert(0, data[begin:sep + 1])
354            prev_begin = begin
355
356    output = ''.join(output)
357    if no_log_values:
358        output = remove_values(output, no_log_values)
359    return output
360
361
362def _load_params():
363    ''' read the modules parameters and store them globally.
364
365    This function may be needed for certain very dynamic custom modules which
366    want to process the parameters that are being handed the module.  Since
367    this is so closely tied to the implementation of modules we cannot
368    guarantee API stability for it (it may change between versions) however we
369    will try not to break it gratuitously.  It is certainly more future-proof
370    to call this function and consume its outputs than to implement the logic
371    inside it as a copy in your own code.
372    '''
373    global _ANSIBLE_ARGS
374    if _ANSIBLE_ARGS is not None:
375        buffer = _ANSIBLE_ARGS
376    else:
377        # debug overrides to read args from file or cmdline
378
379        # Avoid tracebacks when locale is non-utf8
380        # We control the args and we pass them as utf8
381        if len(sys.argv) > 1:
382            if os.path.isfile(sys.argv[1]):
383                fd = open(sys.argv[1], 'rb')
384                buffer = fd.read()
385                fd.close()
386            else:
387                buffer = sys.argv[1]
388                if PY3:
389                    buffer = buffer.encode('utf-8', errors='surrogateescape')
390        # default case, read from stdin
391        else:
392            if PY2:
393                buffer = sys.stdin.read()
394            else:
395                buffer = sys.stdin.buffer.read()
396        _ANSIBLE_ARGS = buffer
397
398    try:
399        params = json.loads(buffer.decode('utf-8'))
400    except ValueError:
401        # This helper used too early for fail_json to work.
402        print('\n{"msg": "Error: Module unable to decode valid JSON on stdin.  Unable to figure out what parameters were passed", "failed": true}')
403        sys.exit(1)
404
405    if PY2:
406        params = json_dict_unicode_to_bytes(params)
407
408    try:
409        return params['ANSIBLE_MODULE_ARGS']
410    except KeyError:
411        # This helper does not have access to fail_json so we have to print
412        # json output on our own.
413        print('\n{"msg": "Error: Module unable to locate ANSIBLE_MODULE_ARGS in json data from stdin.  Unable to figure out what parameters were passed", '
414              '"failed": true}')
415        sys.exit(1)
416
417
418def missing_required_lib(library, reason=None, url=None):
419    hostname = platform.node()
420    msg = "Failed to import the required Python library (%s) on %s's Python %s." % (library, hostname, sys.executable)
421    if reason:
422        msg += " This is required %s." % reason
423    if url:
424        msg += " See %s for more info." % url
425
426    msg += (" Please read the module documentation and install it in the appropriate location."
427            " If the required library is installed, but Ansible is using the wrong Python interpreter,"
428            " please consult the documentation on ansible_python_interpreter")
429    return msg
430
431
432class AnsibleModule(object):
433    def __init__(self, argument_spec, bypass_checks=False, no_log=False,
434                 mutually_exclusive=None, required_together=None,
435                 required_one_of=None, add_file_common_args=False,
436                 supports_check_mode=False, required_if=None, required_by=None):
437
438        '''
439        Common code for quickly building an ansible module in Python
440        (although you can write modules with anything that can return JSON).
441
442        See :ref:`developing_modules_general` for a general introduction
443        and :ref:`developing_program_flow_modules` for more detailed explanation.
444        '''
445
446        self._name = os.path.basename(__file__)  # initialize name until we can parse from options
447        self.argument_spec = argument_spec
448        self.supports_check_mode = supports_check_mode
449        self.check_mode = False
450        self.bypass_checks = bypass_checks
451        self.no_log = no_log
452
453        self.mutually_exclusive = mutually_exclusive
454        self.required_together = required_together
455        self.required_one_of = required_one_of
456        self.required_if = required_if
457        self.required_by = required_by
458        self.cleanup_files = []
459        self._debug = False
460        self._diff = False
461        self._socket_path = None
462        self._shell = None
463        self._syslog_facility = 'LOG_USER'
464        self._verbosity = 0
465        # May be used to set modifications to the environment for any
466        # run_command invocation
467        self.run_command_environ_update = {}
468        self._clean = {}
469        self._string_conversion_action = ''
470
471        self.aliases = {}
472        self._legal_inputs = []
473        self._options_context = list()
474        self._tmpdir = None
475
476        if add_file_common_args:
477            for k, v in FILE_COMMON_ARGUMENTS.items():
478                if k not in self.argument_spec:
479                    self.argument_spec[k] = v
480
481        # Save parameter values that should never be logged
482        self.no_log_values = set()
483
484        # check the locale as set by the current environment, and reset to
485        # a known valid (LANG=C) if it's an invalid/unavailable locale
486        self._check_locale()
487
488        self._load_params()
489        self._set_internal_properties()
490
491        self.validator = ModuleArgumentSpecValidator(self.argument_spec,
492                                                     self.mutually_exclusive,
493                                                     self.required_together,
494                                                     self.required_one_of,
495                                                     self.required_if,
496                                                     self.required_by,
497                                                     )
498
499        self.validation_result = self.validator.validate(self.params)
500        self.params.update(self.validation_result.validated_parameters)
501        self.no_log_values.update(self.validation_result._no_log_values)
502
503        try:
504            error = self.validation_result.errors[0]
505        except IndexError:
506            error = None
507
508        # Fail for validation errors, even in check mode
509        if error:
510            msg = self.validation_result.errors.msg
511            if isinstance(error, UnsupportedError):
512                msg = "Unsupported parameters for ({name}) {kind}: {msg}".format(name=self._name, kind='module', msg=msg)
513
514            self.fail_json(msg=msg)
515
516        if self.check_mode and not self.supports_check_mode:
517            self.exit_json(skipped=True, msg="remote module (%s) does not support check mode" % self._name)
518
519        # This is for backwards compatibility only.
520        self._CHECK_ARGUMENT_TYPES_DISPATCHER = DEFAULT_TYPE_VALIDATORS
521
522        if not self.no_log:
523            self._log_invocation()
524
525        # selinux state caching
526        self._selinux_enabled = None
527        self._selinux_mls_enabled = None
528        self._selinux_initial_context = None
529
530        # finally, make sure we're in a sane working dir
531        self._set_cwd()
532
533    @property
534    def tmpdir(self):
535        # if _ansible_tmpdir was not set and we have a remote_tmp,
536        # the module needs to create it and clean it up once finished.
537        # otherwise we create our own module tmp dir from the system defaults
538        if self._tmpdir is None:
539            basedir = None
540
541            if self._remote_tmp is not None:
542                basedir = os.path.expanduser(os.path.expandvars(self._remote_tmp))
543
544            if basedir is not None and not os.path.exists(basedir):
545                try:
546                    os.makedirs(basedir, mode=0o700)
547                except (OSError, IOError) as e:
548                    self.warn("Unable to use %s as temporary directory, "
549                              "failing back to system: %s" % (basedir, to_native(e)))
550                    basedir = None
551                else:
552                    self.warn("Module remote_tmp %s did not exist and was "
553                              "created with a mode of 0700, this may cause"
554                              " issues when running as another user. To "
555                              "avoid this, create the remote_tmp dir with "
556                              "the correct permissions manually" % basedir)
557
558            basefile = "ansible-moduletmp-%s-" % time.time()
559            try:
560                tmpdir = tempfile.mkdtemp(prefix=basefile, dir=basedir)
561            except (OSError, IOError) as e:
562                self.fail_json(
563                    msg="Failed to create remote module tmp path at dir %s "
564                        "with prefix %s: %s" % (basedir, basefile, to_native(e))
565                )
566            if not self._keep_remote_files:
567                atexit.register(shutil.rmtree, tmpdir)
568            self._tmpdir = tmpdir
569
570        return self._tmpdir
571
572    def warn(self, warning):
573        warn(warning)
574        self.log('[WARNING] %s' % warning)
575
576    def deprecate(self, msg, version=None, date=None, collection_name=None):
577        if version is not None and date is not None:
578            raise AssertionError("implementation error -- version and date must not both be set")
579        deprecate(msg, version=version, date=date, collection_name=collection_name)
580        # For compatibility, we accept that neither version nor date is set,
581        # and treat that the same as if version would haven been set
582        if date is not None:
583            self.log('[DEPRECATION WARNING] %s %s' % (msg, date))
584        else:
585            self.log('[DEPRECATION WARNING] %s %s' % (msg, version))
586
587    def load_file_common_arguments(self, params, path=None):
588        '''
589        many modules deal with files, this encapsulates common
590        options that the file module accepts such that it is directly
591        available to all modules and they can share code.
592
593        Allows to overwrite the path/dest module argument by providing path.
594        '''
595
596        if path is None:
597            path = params.get('path', params.get('dest', None))
598        if path is None:
599            return {}
600        else:
601            path = os.path.expanduser(os.path.expandvars(path))
602
603        b_path = to_bytes(path, errors='surrogate_or_strict')
604        # if the path is a symlink, and we're following links, get
605        # the target of the link instead for testing
606        if params.get('follow', False) and os.path.islink(b_path):
607            b_path = os.path.realpath(b_path)
608            path = to_native(b_path)
609
610        mode = params.get('mode', None)
611        owner = params.get('owner', None)
612        group = params.get('group', None)
613
614        # selinux related options
615        seuser = params.get('seuser', None)
616        serole = params.get('serole', None)
617        setype = params.get('setype', None)
618        selevel = params.get('selevel', None)
619        secontext = [seuser, serole, setype]
620
621        if self.selinux_mls_enabled():
622            secontext.append(selevel)
623
624        default_secontext = self.selinux_default_context(path)
625        for i in range(len(default_secontext)):
626            if i is not None and secontext[i] == '_default':
627                secontext[i] = default_secontext[i]
628
629        attributes = params.get('attributes', None)
630        return dict(
631            path=path, mode=mode, owner=owner, group=group,
632            seuser=seuser, serole=serole, setype=setype,
633            selevel=selevel, secontext=secontext, attributes=attributes,
634        )
635
636    # Detect whether using selinux that is MLS-aware.
637    # While this means you can set the level/range with
638    # selinux.lsetfilecon(), it may or may not mean that you
639    # will get the selevel as part of the context returned
640    # by selinux.lgetfilecon().
641
642    def selinux_mls_enabled(self):
643        if self._selinux_mls_enabled is None:
644            self._selinux_mls_enabled = HAVE_SELINUX and selinux.is_selinux_mls_enabled() == 1
645
646        return self._selinux_mls_enabled
647
648    def selinux_enabled(self):
649        if self._selinux_enabled is None:
650            self._selinux_enabled = HAVE_SELINUX and selinux.is_selinux_enabled() == 1
651
652        return self._selinux_enabled
653
654    # Determine whether we need a placeholder for selevel/mls
655    def selinux_initial_context(self):
656        if self._selinux_initial_context is None:
657            self._selinux_initial_context = [None, None, None]
658            if self.selinux_mls_enabled():
659                self._selinux_initial_context.append(None)
660
661        return self._selinux_initial_context
662
663    # If selinux fails to find a default, return an array of None
664    def selinux_default_context(self, path, mode=0):
665        context = self.selinux_initial_context()
666        if not self.selinux_enabled():
667            return context
668        try:
669            ret = selinux.matchpathcon(to_native(path, errors='surrogate_or_strict'), mode)
670        except OSError:
671            return context
672        if ret[0] == -1:
673            return context
674        # Limit split to 4 because the selevel, the last in the list,
675        # may contain ':' characters
676        context = ret[1].split(':', 3)
677        return context
678
679    def selinux_context(self, path):
680        context = self.selinux_initial_context()
681        if not self.selinux_enabled():
682            return context
683        try:
684            ret = selinux.lgetfilecon_raw(to_native(path, errors='surrogate_or_strict'))
685        except OSError as e:
686            if e.errno == errno.ENOENT:
687                self.fail_json(path=path, msg='path %s does not exist' % path)
688            else:
689                self.fail_json(path=path, msg='failed to retrieve selinux context')
690        if ret[0] == -1:
691            return context
692        # Limit split to 4 because the selevel, the last in the list,
693        # may contain ':' characters
694        context = ret[1].split(':', 3)
695        return context
696
697    def user_and_group(self, path, expand=True):
698        b_path = to_bytes(path, errors='surrogate_or_strict')
699        if expand:
700            b_path = os.path.expanduser(os.path.expandvars(b_path))
701        st = os.lstat(b_path)
702        uid = st.st_uid
703        gid = st.st_gid
704        return (uid, gid)
705
706    def find_mount_point(self, path):
707        '''
708            Takes a path and returns it's mount point
709
710        :param path: a string type with a filesystem path
711        :returns: the path to the mount point as a text type
712        '''
713
714        b_path = os.path.realpath(to_bytes(os.path.expanduser(os.path.expandvars(path)), errors='surrogate_or_strict'))
715        while not os.path.ismount(b_path):
716            b_path = os.path.dirname(b_path)
717
718        return to_text(b_path, errors='surrogate_or_strict')
719
720    def is_special_selinux_path(self, path):
721        """
722        Returns a tuple containing (True, selinux_context) if the given path is on a
723        NFS or other 'special' fs  mount point, otherwise the return will be (False, None).
724        """
725        try:
726            f = open('/proc/mounts', 'r')
727            mount_data = f.readlines()
728            f.close()
729        except Exception:
730            return (False, None)
731
732        path_mount_point = self.find_mount_point(path)
733
734        for line in mount_data:
735            (device, mount_point, fstype, options, rest) = line.split(' ', 4)
736            if to_bytes(path_mount_point) == to_bytes(mount_point):
737                for fs in self._selinux_special_fs:
738                    if fs in fstype:
739                        special_context = self.selinux_context(path_mount_point)
740                        return (True, special_context)
741
742        return (False, None)
743
744    def set_default_selinux_context(self, path, changed):
745        if not self.selinux_enabled():
746            return changed
747        context = self.selinux_default_context(path)
748        return self.set_context_if_different(path, context, False)
749
750    def set_context_if_different(self, path, context, changed, diff=None):
751
752        if not self.selinux_enabled():
753            return changed
754
755        if self.check_file_absent_if_check_mode(path):
756            return True
757
758        cur_context = self.selinux_context(path)
759        new_context = list(cur_context)
760        # Iterate over the current context instead of the
761        # argument context, which may have selevel.
762
763        (is_special_se, sp_context) = self.is_special_selinux_path(path)
764        if is_special_se:
765            new_context = sp_context
766        else:
767            for i in range(len(cur_context)):
768                if len(context) > i:
769                    if context[i] is not None and context[i] != cur_context[i]:
770                        new_context[i] = context[i]
771                    elif context[i] is None:
772                        new_context[i] = cur_context[i]
773
774        if cur_context != new_context:
775            if diff is not None:
776                if 'before' not in diff:
777                    diff['before'] = {}
778                diff['before']['secontext'] = cur_context
779                if 'after' not in diff:
780                    diff['after'] = {}
781                diff['after']['secontext'] = new_context
782
783            try:
784                if self.check_mode:
785                    return True
786                rc = selinux.lsetfilecon(to_native(path), ':'.join(new_context))
787            except OSError as e:
788                self.fail_json(path=path, msg='invalid selinux context: %s' % to_native(e),
789                               new_context=new_context, cur_context=cur_context, input_was=context)
790            if rc != 0:
791                self.fail_json(path=path, msg='set selinux context failed')
792            changed = True
793        return changed
794
795    def set_owner_if_different(self, path, owner, changed, diff=None, expand=True):
796
797        if owner is None:
798            return changed
799
800        b_path = to_bytes(path, errors='surrogate_or_strict')
801        if expand:
802            b_path = os.path.expanduser(os.path.expandvars(b_path))
803
804        if self.check_file_absent_if_check_mode(b_path):
805            return True
806
807        orig_uid, orig_gid = self.user_and_group(b_path, expand)
808        try:
809            uid = int(owner)
810        except ValueError:
811            try:
812                uid = pwd.getpwnam(owner).pw_uid
813            except KeyError:
814                path = to_text(b_path)
815                self.fail_json(path=path, msg='chown failed: failed to look up user %s' % owner)
816
817        if orig_uid != uid:
818            if diff is not None:
819                if 'before' not in diff:
820                    diff['before'] = {}
821                diff['before']['owner'] = orig_uid
822                if 'after' not in diff:
823                    diff['after'] = {}
824                diff['after']['owner'] = uid
825
826            if self.check_mode:
827                return True
828            try:
829                os.lchown(b_path, uid, -1)
830            except (IOError, OSError) as e:
831                path = to_text(b_path)
832                self.fail_json(path=path, msg='chown failed: %s' % (to_text(e)))
833            changed = True
834        return changed
835
836    def set_group_if_different(self, path, group, changed, diff=None, expand=True):
837
838        if group is None:
839            return changed
840
841        b_path = to_bytes(path, errors='surrogate_or_strict')
842        if expand:
843            b_path = os.path.expanduser(os.path.expandvars(b_path))
844
845        if self.check_file_absent_if_check_mode(b_path):
846            return True
847
848        orig_uid, orig_gid = self.user_and_group(b_path, expand)
849        try:
850            gid = int(group)
851        except ValueError:
852            try:
853                gid = grp.getgrnam(group).gr_gid
854            except KeyError:
855                path = to_text(b_path)
856                self.fail_json(path=path, msg='chgrp failed: failed to look up group %s' % group)
857
858        if orig_gid != gid:
859            if diff is not None:
860                if 'before' not in diff:
861                    diff['before'] = {}
862                diff['before']['group'] = orig_gid
863                if 'after' not in diff:
864                    diff['after'] = {}
865                diff['after']['group'] = gid
866
867            if self.check_mode:
868                return True
869            try:
870                os.lchown(b_path, -1, gid)
871            except OSError:
872                path = to_text(b_path)
873                self.fail_json(path=path, msg='chgrp failed')
874            changed = True
875        return changed
876
877    def set_mode_if_different(self, path, mode, changed, diff=None, expand=True):
878
879        if mode is None:
880            return changed
881
882        b_path = to_bytes(path, errors='surrogate_or_strict')
883        if expand:
884            b_path = os.path.expanduser(os.path.expandvars(b_path))
885
886        if self.check_file_absent_if_check_mode(b_path):
887            return True
888
889        path_stat = os.lstat(b_path)
890
891        if not isinstance(mode, int):
892            try:
893                mode = int(mode, 8)
894            except Exception:
895                try:
896                    mode = self._symbolic_mode_to_octal(path_stat, mode)
897                except Exception as e:
898                    path = to_text(b_path)
899                    self.fail_json(path=path,
900                                   msg="mode must be in octal or symbolic form",
901                                   details=to_native(e))
902
903                if mode != stat.S_IMODE(mode):
904                    # prevent mode from having extra info orbeing invalid long number
905                    path = to_text(b_path)
906                    self.fail_json(path=path, msg="Invalid mode supplied, only permission info is allowed", details=mode)
907
908        prev_mode = stat.S_IMODE(path_stat.st_mode)
909
910        if prev_mode != mode:
911
912            if diff is not None:
913                if 'before' not in diff:
914                    diff['before'] = {}
915                diff['before']['mode'] = '0%03o' % prev_mode
916                if 'after' not in diff:
917                    diff['after'] = {}
918                diff['after']['mode'] = '0%03o' % mode
919
920            if self.check_mode:
921                return True
922            # FIXME: comparison against string above will cause this to be executed
923            # every time
924            try:
925                if hasattr(os, 'lchmod'):
926                    os.lchmod(b_path, mode)
927                else:
928                    if not os.path.islink(b_path):
929                        os.chmod(b_path, mode)
930                    else:
931                        # Attempt to set the perms of the symlink but be
932                        # careful not to change the perms of the underlying
933                        # file while trying
934                        underlying_stat = os.stat(b_path)
935                        os.chmod(b_path, mode)
936                        new_underlying_stat = os.stat(b_path)
937                        if underlying_stat.st_mode != new_underlying_stat.st_mode:
938                            os.chmod(b_path, stat.S_IMODE(underlying_stat.st_mode))
939            except OSError as e:
940                if os.path.islink(b_path) and e.errno in (
941                    errno.EACCES,  # can't access symlink in sticky directory (stat)
942                    errno.EPERM,  # can't set mode on symbolic links (chmod)
943                    errno.EROFS,  # can't set mode on read-only filesystem
944                ):
945                    pass
946                elif e.errno in (errno.ENOENT, errno.ELOOP):  # Can't set mode on broken symbolic links
947                    pass
948                else:
949                    raise
950            except Exception as e:
951                path = to_text(b_path)
952                self.fail_json(path=path, msg='chmod failed', details=to_native(e),
953                               exception=traceback.format_exc())
954
955            path_stat = os.lstat(b_path)
956            new_mode = stat.S_IMODE(path_stat.st_mode)
957
958            if new_mode != prev_mode:
959                changed = True
960        return changed
961
962    def set_attributes_if_different(self, path, attributes, changed, diff=None, expand=True):
963
964        if attributes is None:
965            return changed
966
967        b_path = to_bytes(path, errors='surrogate_or_strict')
968        if expand:
969            b_path = os.path.expanduser(os.path.expandvars(b_path))
970
971        if self.check_file_absent_if_check_mode(b_path):
972            return True
973
974        existing = self.get_file_attributes(b_path, include_version=False)
975
976        attr_mod = '='
977        if attributes.startswith(('-', '+')):
978            attr_mod = attributes[0]
979            attributes = attributes[1:]
980
981        if existing.get('attr_flags', '') != attributes or attr_mod == '-':
982            attrcmd = self.get_bin_path('chattr')
983            if attrcmd:
984                attrcmd = [attrcmd, '%s%s' % (attr_mod, attributes), b_path]
985                changed = True
986
987                if diff is not None:
988                    if 'before' not in diff:
989                        diff['before'] = {}
990                    diff['before']['attributes'] = existing.get('attr_flags')
991                    if 'after' not in diff:
992                        diff['after'] = {}
993                    diff['after']['attributes'] = '%s%s' % (attr_mod, attributes)
994
995                if not self.check_mode:
996                    try:
997                        rc, out, err = self.run_command(attrcmd)
998                        if rc != 0 or err:
999                            raise Exception("Error while setting attributes: %s" % (out + err))
1000                    except Exception as e:
1001                        self.fail_json(path=to_text(b_path), msg='chattr failed',
1002                                       details=to_native(e), exception=traceback.format_exc())
1003        return changed
1004
1005    def get_file_attributes(self, path, include_version=True):
1006        output = {}
1007        attrcmd = self.get_bin_path('lsattr', False)
1008        if attrcmd:
1009            flags = '-vd' if include_version else '-d'
1010            attrcmd = [attrcmd, flags, path]
1011            try:
1012                rc, out, err = self.run_command(attrcmd)
1013                if rc == 0:
1014                    res = out.split()
1015                    attr_flags_idx = 0
1016                    if include_version:
1017                        attr_flags_idx = 1
1018                        output['version'] = res[0].strip()
1019                    output['attr_flags'] = res[attr_flags_idx].replace('-', '').strip()
1020                    output['attributes'] = format_attributes(output['attr_flags'])
1021            except Exception:
1022                pass
1023        return output
1024
1025    @classmethod
1026    def _symbolic_mode_to_octal(cls, path_stat, symbolic_mode):
1027        """
1028        This enables symbolic chmod string parsing as stated in the chmod man-page
1029
1030        This includes things like: "u=rw-x+X,g=r-x+X,o=r-x+X"
1031        """
1032
1033        new_mode = stat.S_IMODE(path_stat.st_mode)
1034
1035        # Now parse all symbolic modes
1036        for mode in symbolic_mode.split(','):
1037            # Per single mode. This always contains a '+', '-' or '='
1038            # Split it on that
1039            permlist = MODE_OPERATOR_RE.split(mode)
1040
1041            # And find all the operators
1042            opers = MODE_OPERATOR_RE.findall(mode)
1043
1044            # The user(s) where it's all about is the first element in the
1045            # 'permlist' list. Take that and remove it from the list.
1046            # An empty user or 'a' means 'all'.
1047            users = permlist.pop(0)
1048            use_umask = (users == '')
1049            if users == 'a' or users == '':
1050                users = 'ugo'
1051
1052            # Check if there are illegal characters in the user list
1053            # They can end up in 'users' because they are not split
1054            if USERS_RE.match(users):
1055                raise ValueError("bad symbolic permission for mode: %s" % mode)
1056
1057            # Now we have two list of equal length, one contains the requested
1058            # permissions and one with the corresponding operators.
1059            for idx, perms in enumerate(permlist):
1060                # Check if there are illegal characters in the permissions
1061                if PERMS_RE.match(perms):
1062                    raise ValueError("bad symbolic permission for mode: %s" % mode)
1063
1064                for user in users:
1065                    mode_to_apply = cls._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask)
1066                    new_mode = cls._apply_operation_to_mode(user, opers[idx], mode_to_apply, new_mode)
1067
1068        return new_mode
1069
1070    @staticmethod
1071    def _apply_operation_to_mode(user, operator, mode_to_apply, current_mode):
1072        if operator == '=':
1073            if user == 'u':
1074                mask = stat.S_IRWXU | stat.S_ISUID
1075            elif user == 'g':
1076                mask = stat.S_IRWXG | stat.S_ISGID
1077            elif user == 'o':
1078                mask = stat.S_IRWXO | stat.S_ISVTX
1079
1080            # mask out u, g, or o permissions from current_mode and apply new permissions
1081            inverse_mask = mask ^ PERM_BITS
1082            new_mode = (current_mode & inverse_mask) | mode_to_apply
1083        elif operator == '+':
1084            new_mode = current_mode | mode_to_apply
1085        elif operator == '-':
1086            new_mode = current_mode - (current_mode & mode_to_apply)
1087        return new_mode
1088
1089    @staticmethod
1090    def _get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask):
1091        prev_mode = stat.S_IMODE(path_stat.st_mode)
1092
1093        is_directory = stat.S_ISDIR(path_stat.st_mode)
1094        has_x_permissions = (prev_mode & EXEC_PERM_BITS) > 0
1095        apply_X_permission = is_directory or has_x_permissions
1096
1097        # Get the umask, if the 'user' part is empty, the effect is as if (a) were
1098        # given, but bits that are set in the umask are not affected.
1099        # We also need the "reversed umask" for masking
1100        umask = os.umask(0)
1101        os.umask(umask)
1102        rev_umask = umask ^ PERM_BITS
1103
1104        # Permission bits constants documented at:
1105        # http://docs.python.org/2/library/stat.html#stat.S_ISUID
1106        if apply_X_permission:
1107            X_perms = {
1108                'u': {'X': stat.S_IXUSR},
1109                'g': {'X': stat.S_IXGRP},
1110                'o': {'X': stat.S_IXOTH},
1111            }
1112        else:
1113            X_perms = {
1114                'u': {'X': 0},
1115                'g': {'X': 0},
1116                'o': {'X': 0},
1117            }
1118
1119        user_perms_to_modes = {
1120            'u': {
1121                'r': rev_umask & stat.S_IRUSR if use_umask else stat.S_IRUSR,
1122                'w': rev_umask & stat.S_IWUSR if use_umask else stat.S_IWUSR,
1123                'x': rev_umask & stat.S_IXUSR if use_umask else stat.S_IXUSR,
1124                's': stat.S_ISUID,
1125                't': 0,
1126                'u': prev_mode & stat.S_IRWXU,
1127                'g': (prev_mode & stat.S_IRWXG) << 3,
1128                'o': (prev_mode & stat.S_IRWXO) << 6},
1129            'g': {
1130                'r': rev_umask & stat.S_IRGRP if use_umask else stat.S_IRGRP,
1131                'w': rev_umask & stat.S_IWGRP if use_umask else stat.S_IWGRP,
1132                'x': rev_umask & stat.S_IXGRP if use_umask else stat.S_IXGRP,
1133                's': stat.S_ISGID,
1134                't': 0,
1135                'u': (prev_mode & stat.S_IRWXU) >> 3,
1136                'g': prev_mode & stat.S_IRWXG,
1137                'o': (prev_mode & stat.S_IRWXO) << 3},
1138            'o': {
1139                'r': rev_umask & stat.S_IROTH if use_umask else stat.S_IROTH,
1140                'w': rev_umask & stat.S_IWOTH if use_umask else stat.S_IWOTH,
1141                'x': rev_umask & stat.S_IXOTH if use_umask else stat.S_IXOTH,
1142                's': 0,
1143                't': stat.S_ISVTX,
1144                'u': (prev_mode & stat.S_IRWXU) >> 6,
1145                'g': (prev_mode & stat.S_IRWXG) >> 3,
1146                'o': prev_mode & stat.S_IRWXO},
1147        }
1148
1149        # Insert X_perms into user_perms_to_modes
1150        for key, value in X_perms.items():
1151            user_perms_to_modes[key].update(value)
1152
1153        def or_reduce(mode, perm):
1154            return mode | user_perms_to_modes[user][perm]
1155
1156        return reduce(or_reduce, perms, 0)
1157
1158    def set_fs_attributes_if_different(self, file_args, changed, diff=None, expand=True):
1159        # set modes owners and context as needed
1160        changed = self.set_context_if_different(
1161            file_args['path'], file_args['secontext'], changed, diff
1162        )
1163        changed = self.set_owner_if_different(
1164            file_args['path'], file_args['owner'], changed, diff, expand
1165        )
1166        changed = self.set_group_if_different(
1167            file_args['path'], file_args['group'], changed, diff, expand
1168        )
1169        changed = self.set_mode_if_different(
1170            file_args['path'], file_args['mode'], changed, diff, expand
1171        )
1172        changed = self.set_attributes_if_different(
1173            file_args['path'], file_args['attributes'], changed, diff, expand
1174        )
1175        return changed
1176
1177    def check_file_absent_if_check_mode(self, file_path):
1178        return self.check_mode and not os.path.exists(file_path)
1179
1180    def set_directory_attributes_if_different(self, file_args, changed, diff=None, expand=True):
1181        return self.set_fs_attributes_if_different(file_args, changed, diff, expand)
1182
1183    def set_file_attributes_if_different(self, file_args, changed, diff=None, expand=True):
1184        return self.set_fs_attributes_if_different(file_args, changed, diff, expand)
1185
1186    def add_path_info(self, kwargs):
1187        '''
1188        for results that are files, supplement the info about the file
1189        in the return path with stats about the file path.
1190        '''
1191
1192        path = kwargs.get('path', kwargs.get('dest', None))
1193        if path is None:
1194            return kwargs
1195        b_path = to_bytes(path, errors='surrogate_or_strict')
1196        if os.path.exists(b_path):
1197            (uid, gid) = self.user_and_group(path)
1198            kwargs['uid'] = uid
1199            kwargs['gid'] = gid
1200            try:
1201                user = pwd.getpwuid(uid)[0]
1202            except KeyError:
1203                user = str(uid)
1204            try:
1205                group = grp.getgrgid(gid)[0]
1206            except KeyError:
1207                group = str(gid)
1208            kwargs['owner'] = user
1209            kwargs['group'] = group
1210            st = os.lstat(b_path)
1211            kwargs['mode'] = '0%03o' % stat.S_IMODE(st[stat.ST_MODE])
1212            # secontext not yet supported
1213            if os.path.islink(b_path):
1214                kwargs['state'] = 'link'
1215            elif os.path.isdir(b_path):
1216                kwargs['state'] = 'directory'
1217            elif os.stat(b_path).st_nlink > 1:
1218                kwargs['state'] = 'hard'
1219            else:
1220                kwargs['state'] = 'file'
1221            if self.selinux_enabled():
1222                kwargs['secontext'] = ':'.join(self.selinux_context(path))
1223            kwargs['size'] = st[stat.ST_SIZE]
1224        return kwargs
1225
1226    def _check_locale(self):
1227        '''
1228        Uses the locale module to test the currently set locale
1229        (per the LANG and LC_CTYPE environment settings)
1230        '''
1231        try:
1232            # setting the locale to '' uses the default locale
1233            # as it would be returned by locale.getdefaultlocale()
1234            locale.setlocale(locale.LC_ALL, '')
1235        except locale.Error:
1236            # fallback to the 'C' locale, which may cause unicode
1237            # issues but is preferable to simply failing because
1238            # of an unknown locale
1239            locale.setlocale(locale.LC_ALL, 'C')
1240            os.environ['LANG'] = 'C'
1241            os.environ['LC_ALL'] = 'C'
1242            os.environ['LC_MESSAGES'] = 'C'
1243        except Exception as e:
1244            self.fail_json(msg="An unknown error was encountered while attempting to validate the locale: %s" %
1245                           to_native(e), exception=traceback.format_exc())
1246
1247    def _set_internal_properties(self, argument_spec=None, module_parameters=None):
1248        if argument_spec is None:
1249            argument_spec = self.argument_spec
1250        if module_parameters is None:
1251            module_parameters = self.params
1252
1253        for k in PASS_VARS:
1254            # handle setting internal properties from internal ansible vars
1255            param_key = '_ansible_%s' % k
1256            if param_key in module_parameters:
1257                if k in PASS_BOOLS:
1258                    setattr(self, PASS_VARS[k][0], self.boolean(module_parameters[param_key]))
1259                else:
1260                    setattr(self, PASS_VARS[k][0], module_parameters[param_key])
1261
1262                # clean up internal top level params:
1263                if param_key in self.params:
1264                    del self.params[param_key]
1265            else:
1266                # use defaults if not already set
1267                if not hasattr(self, PASS_VARS[k][0]):
1268                    setattr(self, PASS_VARS[k][0], PASS_VARS[k][1])
1269
1270    def safe_eval(self, value, locals=None, include_exceptions=False):
1271        return safe_eval(value, locals, include_exceptions)
1272
1273    def _load_params(self):
1274        ''' read the input and set the params attribute.
1275
1276        This method is for backwards compatibility.  The guts of the function
1277        were moved out in 2.1 so that custom modules could read the parameters.
1278        '''
1279        # debug overrides to read args from file or cmdline
1280        self.params = _load_params()
1281
1282    def _log_to_syslog(self, msg):
1283        if HAS_SYSLOG:
1284            try:
1285                module = 'ansible-%s' % self._name
1286                facility = getattr(syslog, self._syslog_facility, syslog.LOG_USER)
1287                syslog.openlog(str(module), 0, facility)
1288                syslog.syslog(syslog.LOG_INFO, msg)
1289            except TypeError as e:
1290                self.fail_json(
1291                    msg='Failed to log to syslog (%s). To proceed anyway, '
1292                        'disable syslog logging by setting no_target_syslog '
1293                        'to True in your Ansible config.' % to_native(e),
1294                    exception=traceback.format_exc(),
1295                    msg_to_log=msg,
1296                )
1297
1298    def debug(self, msg):
1299        if self._debug:
1300            self.log('[debug] %s' % msg)
1301
1302    def log(self, msg, log_args=None):
1303
1304        if not self.no_log:
1305
1306            if log_args is None:
1307                log_args = dict()
1308
1309            module = 'ansible-%s' % self._name
1310            if isinstance(module, binary_type):
1311                module = module.decode('utf-8', 'replace')
1312
1313            # 6655 - allow for accented characters
1314            if not isinstance(msg, (binary_type, text_type)):
1315                raise TypeError("msg should be a string (got %s)" % type(msg))
1316
1317            # We want journal to always take text type
1318            # syslog takes bytes on py2, text type on py3
1319            if isinstance(msg, binary_type):
1320                journal_msg = remove_values(msg.decode('utf-8', 'replace'), self.no_log_values)
1321            else:
1322                # TODO: surrogateescape is a danger here on Py3
1323                journal_msg = remove_values(msg, self.no_log_values)
1324
1325            if PY3:
1326                syslog_msg = journal_msg
1327            else:
1328                syslog_msg = journal_msg.encode('utf-8', 'replace')
1329
1330            if has_journal:
1331                journal_args = [("MODULE", os.path.basename(__file__))]
1332                for arg in log_args:
1333                    journal_args.append((arg.upper(), str(log_args[arg])))
1334                try:
1335                    if HAS_SYSLOG:
1336                        # If syslog_facility specified, it needs to convert
1337                        #  from the facility name to the facility code, and
1338                        #  set it as SYSLOG_FACILITY argument of journal.send()
1339                        facility = getattr(syslog,
1340                                           self._syslog_facility,
1341                                           syslog.LOG_USER) >> 3
1342                        journal.send(MESSAGE=u"%s %s" % (module, journal_msg),
1343                                     SYSLOG_FACILITY=facility,
1344                                     **dict(journal_args))
1345                    else:
1346                        journal.send(MESSAGE=u"%s %s" % (module, journal_msg),
1347                                     **dict(journal_args))
1348                except IOError:
1349                    # fall back to syslog since logging to journal failed
1350                    self._log_to_syslog(syslog_msg)
1351            else:
1352                self._log_to_syslog(syslog_msg)
1353
1354    def _log_invocation(self):
1355        ''' log that ansible ran the module '''
1356        # TODO: generalize a separate log function and make log_invocation use it
1357        # Sanitize possible password argument when logging.
1358        log_args = dict()
1359
1360        for param in self.params:
1361            canon = self.aliases.get(param, param)
1362            arg_opts = self.argument_spec.get(canon, {})
1363            no_log = arg_opts.get('no_log', None)
1364
1365            # try to proactively capture password/passphrase fields
1366            if no_log is None and PASSWORD_MATCH.search(param):
1367                log_args[param] = 'NOT_LOGGING_PASSWORD'
1368                self.warn('Module did not set no_log for %s' % param)
1369            elif self.boolean(no_log):
1370                log_args[param] = 'NOT_LOGGING_PARAMETER'
1371            else:
1372                param_val = self.params[param]
1373                if not isinstance(param_val, (text_type, binary_type)):
1374                    param_val = str(param_val)
1375                elif isinstance(param_val, text_type):
1376                    param_val = param_val.encode('utf-8')
1377                log_args[param] = heuristic_log_sanitize(param_val, self.no_log_values)
1378
1379        msg = ['%s=%s' % (to_native(arg), to_native(val)) for arg, val in log_args.items()]
1380        if msg:
1381            msg = 'Invoked with %s' % ' '.join(msg)
1382        else:
1383            msg = 'Invoked'
1384
1385        self.log(msg, log_args=log_args)
1386
1387    def _set_cwd(self):
1388        try:
1389            cwd = os.getcwd()
1390            if not os.access(cwd, os.F_OK | os.R_OK):
1391                raise Exception()
1392            return cwd
1393        except Exception:
1394            # we don't have access to the cwd, probably because of sudo.
1395            # Try and move to a neutral location to prevent errors
1396            for cwd in [self.tmpdir, os.path.expandvars('$HOME'), tempfile.gettempdir()]:
1397                try:
1398                    if os.access(cwd, os.F_OK | os.R_OK):
1399                        os.chdir(cwd)
1400                        return cwd
1401                except Exception:
1402                    pass
1403        # we won't error here, as it may *not* be a problem,
1404        # and we don't want to break modules unnecessarily
1405        return None
1406
1407    def get_bin_path(self, arg, required=False, opt_dirs=None):
1408        '''
1409        Find system executable in PATH.
1410
1411        :param arg: The executable to find.
1412        :param required: if executable is not found and required is ``True``, fail_json
1413        :param opt_dirs: optional list of directories to search in addition to ``PATH``
1414        :returns: if found return full path; otherwise return None
1415        '''
1416
1417        bin_path = None
1418        try:
1419            bin_path = get_bin_path(arg=arg, opt_dirs=opt_dirs)
1420        except ValueError as e:
1421            if required:
1422                self.fail_json(msg=to_text(e))
1423            else:
1424                return bin_path
1425
1426        return bin_path
1427
1428    def boolean(self, arg):
1429        '''Convert the argument to a boolean'''
1430        if arg is None:
1431            return arg
1432
1433        try:
1434            return boolean(arg)
1435        except TypeError as e:
1436            self.fail_json(msg=to_native(e))
1437
1438    def jsonify(self, data):
1439        try:
1440            return jsonify(data)
1441        except UnicodeError as e:
1442            self.fail_json(msg=to_text(e))
1443
1444    def from_json(self, data):
1445        return json.loads(data)
1446
1447    def add_cleanup_file(self, path):
1448        if path not in self.cleanup_files:
1449            self.cleanup_files.append(path)
1450
1451    def do_cleanup_files(self):
1452        for path in self.cleanup_files:
1453            self.cleanup(path)
1454
1455    def _return_formatted(self, kwargs):
1456
1457        self.add_path_info(kwargs)
1458
1459        if 'invocation' not in kwargs:
1460            kwargs['invocation'] = {'module_args': self.params}
1461
1462        if 'warnings' in kwargs:
1463            if isinstance(kwargs['warnings'], list):
1464                for w in kwargs['warnings']:
1465                    self.warn(w)
1466            else:
1467                self.warn(kwargs['warnings'])
1468
1469        warnings = get_warning_messages()
1470        if warnings:
1471            kwargs['warnings'] = warnings
1472
1473        if 'deprecations' in kwargs:
1474            if isinstance(kwargs['deprecations'], list):
1475                for d in kwargs['deprecations']:
1476                    if isinstance(d, SEQUENCETYPE) and len(d) == 2:
1477                        self.deprecate(d[0], version=d[1])
1478                    elif isinstance(d, Mapping):
1479                        self.deprecate(d['msg'], version=d.get('version'), date=d.get('date'),
1480                                       collection_name=d.get('collection_name'))
1481                    else:
1482                        self.deprecate(d)  # pylint: disable=ansible-deprecated-no-version
1483            else:
1484                self.deprecate(kwargs['deprecations'])  # pylint: disable=ansible-deprecated-no-version
1485
1486        deprecations = get_deprecation_messages()
1487        if deprecations:
1488            kwargs['deprecations'] = deprecations
1489
1490        kwargs = remove_values(kwargs, self.no_log_values)
1491        print('\n%s' % self.jsonify(kwargs))
1492
1493    def exit_json(self, **kwargs):
1494        ''' return from the module, without error '''
1495
1496        self.do_cleanup_files()
1497        self._return_formatted(kwargs)
1498        sys.exit(0)
1499
1500    def fail_json(self, msg, **kwargs):
1501        ''' return from the module, with an error message '''
1502
1503        kwargs['failed'] = True
1504        kwargs['msg'] = msg
1505
1506        # Add traceback if debug or high verbosity and it is missing
1507        # NOTE: Badly named as exception, it really always has been a traceback
1508        if 'exception' not in kwargs and sys.exc_info()[2] and (self._debug or self._verbosity >= 3):
1509            if PY2:
1510                # On Python 2 this is the last (stack frame) exception and as such may be unrelated to the failure
1511                kwargs['exception'] = 'WARNING: The below traceback may *not* be related to the actual failure.\n' +\
1512                                      ''.join(traceback.format_tb(sys.exc_info()[2]))
1513            else:
1514                kwargs['exception'] = ''.join(traceback.format_tb(sys.exc_info()[2]))
1515
1516        self.do_cleanup_files()
1517        self._return_formatted(kwargs)
1518        sys.exit(1)
1519
1520    def fail_on_missing_params(self, required_params=None):
1521        if not required_params:
1522            return
1523        try:
1524            check_missing_parameters(self.params, required_params)
1525        except TypeError as e:
1526            self.fail_json(msg=to_native(e))
1527
1528    def digest_from_file(self, filename, algorithm):
1529        ''' Return hex digest of local file for a digest_method specified by name, or None if file is not present. '''
1530        b_filename = to_bytes(filename, errors='surrogate_or_strict')
1531
1532        if not os.path.exists(b_filename):
1533            return None
1534        if os.path.isdir(b_filename):
1535            self.fail_json(msg="attempted to take checksum of directory: %s" % filename)
1536
1537        # preserve old behaviour where the third parameter was a hash algorithm object
1538        if hasattr(algorithm, 'hexdigest'):
1539            digest_method = algorithm
1540        else:
1541            try:
1542                digest_method = AVAILABLE_HASH_ALGORITHMS[algorithm]()
1543            except KeyError:
1544                self.fail_json(msg="Could not hash file '%s' with algorithm '%s'. Available algorithms: %s" %
1545                                   (filename, algorithm, ', '.join(AVAILABLE_HASH_ALGORITHMS)))
1546
1547        blocksize = 64 * 1024
1548        infile = open(os.path.realpath(b_filename), 'rb')
1549        block = infile.read(blocksize)
1550        while block:
1551            digest_method.update(block)
1552            block = infile.read(blocksize)
1553        infile.close()
1554        return digest_method.hexdigest()
1555
1556    def md5(self, filename):
1557        ''' Return MD5 hex digest of local file using digest_from_file().
1558
1559        Do not use this function unless you have no other choice for:
1560            1) Optional backwards compatibility
1561            2) Compatibility with a third party protocol
1562
1563        This function will not work on systems complying with FIPS-140-2.
1564
1565        Most uses of this function can use the module.sha1 function instead.
1566        '''
1567        if 'md5' not in AVAILABLE_HASH_ALGORITHMS:
1568            raise ValueError('MD5 not available.  Possibly running in FIPS mode')
1569        return self.digest_from_file(filename, 'md5')
1570
1571    def sha1(self, filename):
1572        ''' Return SHA1 hex digest of local file using digest_from_file(). '''
1573        return self.digest_from_file(filename, 'sha1')
1574
1575    def sha256(self, filename):
1576        ''' Return SHA-256 hex digest of local file using digest_from_file(). '''
1577        return self.digest_from_file(filename, 'sha256')
1578
1579    def backup_local(self, fn):
1580        '''make a date-marked backup of the specified file, return True or False on success or failure'''
1581
1582        backupdest = ''
1583        if os.path.exists(fn):
1584            # backups named basename.PID.YYYY-MM-DD@HH:MM:SS~
1585            ext = time.strftime("%Y-%m-%d@%H:%M:%S~", time.localtime(time.time()))
1586            backupdest = '%s.%s.%s' % (fn, os.getpid(), ext)
1587
1588            try:
1589                self.preserved_copy(fn, backupdest)
1590            except (shutil.Error, IOError) as e:
1591                self.fail_json(msg='Could not make backup of %s to %s: %s' % (fn, backupdest, to_native(e)))
1592
1593        return backupdest
1594
1595    def cleanup(self, tmpfile):
1596        if os.path.exists(tmpfile):
1597            try:
1598                os.unlink(tmpfile)
1599            except OSError as e:
1600                sys.stderr.write("could not cleanup %s: %s" % (tmpfile, to_native(e)))
1601
1602    def preserved_copy(self, src, dest):
1603        """Copy a file with preserved ownership, permissions and context"""
1604
1605        # shutil.copy2(src, dst)
1606        #   Similar to shutil.copy(), but metadata is copied as well - in fact,
1607        #   this is just shutil.copy() followed by copystat(). This is similar
1608        #   to the Unix command cp -p.
1609        #
1610        # shutil.copystat(src, dst)
1611        #   Copy the permission bits, last access time, last modification time,
1612        #   and flags from src to dst. The file contents, owner, and group are
1613        #   unaffected. src and dst are path names given as strings.
1614
1615        shutil.copy2(src, dest)
1616
1617        # Set the context
1618        if self.selinux_enabled():
1619            context = self.selinux_context(src)
1620            self.set_context_if_different(dest, context, False)
1621
1622        # chown it
1623        try:
1624            dest_stat = os.stat(src)
1625            tmp_stat = os.stat(dest)
1626            if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid):
1627                os.chown(dest, dest_stat.st_uid, dest_stat.st_gid)
1628        except OSError as e:
1629            if e.errno != errno.EPERM:
1630                raise
1631
1632        # Set the attributes
1633        current_attribs = self.get_file_attributes(src, include_version=False)
1634        current_attribs = current_attribs.get('attr_flags', '')
1635        self.set_attributes_if_different(dest, current_attribs, True)
1636
1637    def atomic_move(self, src, dest, unsafe_writes=False):
1638        '''atomically move src to dest, copying attributes from dest, returns true on success
1639        it uses os.rename to ensure this as it is an atomic operation, rest of the function is
1640        to work around limitations, corner cases and ensure selinux context is saved if possible'''
1641        context = None
1642        dest_stat = None
1643        b_src = to_bytes(src, errors='surrogate_or_strict')
1644        b_dest = to_bytes(dest, errors='surrogate_or_strict')
1645        if os.path.exists(b_dest):
1646            try:
1647                dest_stat = os.stat(b_dest)
1648
1649                # copy mode and ownership
1650                os.chmod(b_src, dest_stat.st_mode & PERM_BITS)
1651                os.chown(b_src, dest_stat.st_uid, dest_stat.st_gid)
1652
1653                # try to copy flags if possible
1654                if hasattr(os, 'chflags') and hasattr(dest_stat, 'st_flags'):
1655                    try:
1656                        os.chflags(b_src, dest_stat.st_flags)
1657                    except OSError as e:
1658                        for err in 'EOPNOTSUPP', 'ENOTSUP':
1659                            if hasattr(errno, err) and e.errno == getattr(errno, err):
1660                                break
1661                        else:
1662                            raise
1663            except OSError as e:
1664                if e.errno != errno.EPERM:
1665                    raise
1666            if self.selinux_enabled():
1667                context = self.selinux_context(dest)
1668        else:
1669            if self.selinux_enabled():
1670                context = self.selinux_default_context(dest)
1671
1672        creating = not os.path.exists(b_dest)
1673
1674        try:
1675            # Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
1676            os.rename(b_src, b_dest)
1677        except (IOError, OSError) as e:
1678            if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY]:
1679                # only try workarounds for errno 18 (cross device), 1 (not permitted),  13 (permission denied)
1680                # and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
1681                self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)), exception=traceback.format_exc())
1682            else:
1683                # Use bytes here.  In the shippable CI, this fails with
1684                # a UnicodeError with surrogateescape'd strings for an unknown
1685                # reason (doesn't happen in a local Ubuntu16.04 VM)
1686                b_dest_dir = os.path.dirname(b_dest)
1687                b_suffix = os.path.basename(b_dest)
1688                error_msg = None
1689                tmp_dest_name = None
1690                try:
1691                    tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
1692                except (OSError, IOError) as e:
1693                    error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), to_native(e))
1694                except TypeError:
1695                    # We expect that this is happening because python3.4.x and
1696                    # below can't handle byte strings in mkstemp().
1697                    # Traceback would end in something like:
1698                    #     file = _os.path.join(dir, pre + name + suf)
1699                    # TypeError: can't concat bytes to str
1700                    error_msg = ('Failed creating tmp file for atomic move.  This usually happens when using Python3 less than Python3.5. '
1701                                 'Please use Python2.x or Python3.5 or greater.')
1702                finally:
1703                    if error_msg:
1704                        if unsafe_writes:
1705                            self._unsafe_writes(b_src, b_dest)
1706                        else:
1707                            self.fail_json(msg=error_msg, exception=traceback.format_exc())
1708
1709                if tmp_dest_name:
1710                    b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict')
1711
1712                    try:
1713                        try:
1714                            # close tmp file handle before file operations to prevent text file busy errors on vboxfs synced folders (windows host)
1715                            os.close(tmp_dest_fd)
1716                            # leaves tmp file behind when sudo and not root
1717                            try:
1718                                shutil.move(b_src, b_tmp_dest_name)
1719                            except OSError:
1720                                # cleanup will happen by 'rm' of tmpdir
1721                                # copy2 will preserve some metadata
1722                                shutil.copy2(b_src, b_tmp_dest_name)
1723
1724                            if self.selinux_enabled():
1725                                self.set_context_if_different(
1726                                    b_tmp_dest_name, context, False)
1727                            try:
1728                                tmp_stat = os.stat(b_tmp_dest_name)
1729                                if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid):
1730                                    os.chown(b_tmp_dest_name, dest_stat.st_uid, dest_stat.st_gid)
1731                            except OSError as e:
1732                                if e.errno != errno.EPERM:
1733                                    raise
1734                            try:
1735                                os.rename(b_tmp_dest_name, b_dest)
1736                            except (shutil.Error, OSError, IOError) as e:
1737                                if unsafe_writes and e.errno == errno.EBUSY:
1738                                    self._unsafe_writes(b_tmp_dest_name, b_dest)
1739                                else:
1740                                    self.fail_json(msg='Unable to make %s into to %s, failed final rename from %s: %s' %
1741                                                       (src, dest, b_tmp_dest_name, to_native(e)), exception=traceback.format_exc())
1742                        except (shutil.Error, OSError, IOError) as e:
1743                            if unsafe_writes:
1744                                self._unsafe_writes(b_src, b_dest)
1745                            else:
1746                                self.fail_json(msg='Failed to replace file: %s to %s: %s' % (src, dest, to_native(e)), exception=traceback.format_exc())
1747                    finally:
1748                        self.cleanup(b_tmp_dest_name)
1749
1750        if creating:
1751            # make sure the file has the correct permissions
1752            # based on the current value of umask
1753            umask = os.umask(0)
1754            os.umask(umask)
1755            os.chmod(b_dest, DEFAULT_PERM & ~umask)
1756            try:
1757                os.chown(b_dest, os.geteuid(), os.getegid())
1758            except OSError:
1759                # We're okay with trying our best here.  If the user is not
1760                # root (or old Unices) they won't be able to chown.
1761                pass
1762
1763        if self.selinux_enabled():
1764            # rename might not preserve context
1765            self.set_context_if_different(dest, context, False)
1766
1767    def _unsafe_writes(self, src, dest):
1768        # sadly there are some situations where we cannot ensure atomicity, but only if
1769        # the user insists and we get the appropriate error we update the file unsafely
1770        try:
1771            out_dest = in_src = None
1772            try:
1773                out_dest = open(dest, 'wb')
1774                in_src = open(src, 'rb')
1775                shutil.copyfileobj(in_src, out_dest)
1776            finally:  # assuring closed files in 2.4 compatible way
1777                if out_dest:
1778                    out_dest.close()
1779                if in_src:
1780                    in_src.close()
1781        except (shutil.Error, OSError, IOError) as e:
1782            self.fail_json(msg='Could not write data to file (%s) from (%s): %s' % (dest, src, to_native(e)),
1783                           exception=traceback.format_exc())
1784
1785    def _clean_args(self, args):
1786
1787        if not self._clean:
1788            # create a printable version of the command for use in reporting later,
1789            # which strips out things like passwords from the args list
1790            to_clean_args = args
1791            if PY2:
1792                if isinstance(args, text_type):
1793                    to_clean_args = to_bytes(args)
1794            else:
1795                if isinstance(args, binary_type):
1796                    to_clean_args = to_text(args)
1797            if isinstance(args, (text_type, binary_type)):
1798                to_clean_args = shlex.split(to_clean_args)
1799
1800            clean_args = []
1801            is_passwd = False
1802            for arg in (to_native(a) for a in to_clean_args):
1803                if is_passwd:
1804                    is_passwd = False
1805                    clean_args.append('********')
1806                    continue
1807                if PASSWD_ARG_RE.match(arg):
1808                    sep_idx = arg.find('=')
1809                    if sep_idx > -1:
1810                        clean_args.append('%s=********' % arg[:sep_idx])
1811                        continue
1812                    else:
1813                        is_passwd = True
1814                arg = heuristic_log_sanitize(arg, self.no_log_values)
1815                clean_args.append(arg)
1816            self._clean = ' '.join(shlex_quote(arg) for arg in clean_args)
1817
1818        return self._clean
1819
1820    def _restore_signal_handlers(self):
1821        # Reset SIGPIPE to SIG_DFL, otherwise in Python2.7 it gets ignored in subprocesses.
1822        if PY2 and sys.platform != 'win32':
1823            signal.signal(signal.SIGPIPE, signal.SIG_DFL)
1824
1825    def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None,
1826                    use_unsafe_shell=False, prompt_regex=None, environ_update=None, umask=None, encoding='utf-8', errors='surrogate_or_strict',
1827                    expand_user_and_vars=True, pass_fds=None, before_communicate_callback=None, ignore_invalid_cwd=True):
1828        '''
1829        Execute a command, returns rc, stdout, and stderr.
1830
1831        :arg args: is the command to run
1832            * If args is a list, the command will be run with shell=False.
1833            * If args is a string and use_unsafe_shell=False it will split args to a list and run with shell=False
1834            * If args is a string and use_unsafe_shell=True it runs with shell=True.
1835        :kw check_rc: Whether to call fail_json in case of non zero RC.
1836            Default False
1837        :kw close_fds: See documentation for subprocess.Popen(). Default True
1838        :kw executable: See documentation for subprocess.Popen(). Default None
1839        :kw data: If given, information to write to the stdin of the command
1840        :kw binary_data: If False, append a newline to the data.  Default False
1841        :kw path_prefix: If given, additional path to find the command in.
1842            This adds to the PATH environment variable so helper commands in
1843            the same directory can also be found
1844        :kw cwd: If given, working directory to run the command inside
1845        :kw use_unsafe_shell: See `args` parameter.  Default False
1846        :kw prompt_regex: Regex string (not a compiled regex) which can be
1847            used to detect prompts in the stdout which would otherwise cause
1848            the execution to hang (especially if no input data is specified)
1849        :kw environ_update: dictionary to *update* os.environ with
1850        :kw umask: Umask to be used when running the command. Default None
1851        :kw encoding: Since we return native strings, on python3 we need to
1852            know the encoding to use to transform from bytes to text.  If you
1853            want to always get bytes back, use encoding=None.  The default is
1854            "utf-8".  This does not affect transformation of strings given as
1855            args.
1856        :kw errors: Since we return native strings, on python3 we need to
1857            transform stdout and stderr from bytes to text.  If the bytes are
1858            undecodable in the ``encoding`` specified, then use this error
1859            handler to deal with them.  The default is ``surrogate_or_strict``
1860            which means that the bytes will be decoded using the
1861            surrogateescape error handler if available (available on all
1862            python3 versions we support) otherwise a UnicodeError traceback
1863            will be raised.  This does not affect transformations of strings
1864            given as args.
1865        :kw expand_user_and_vars: When ``use_unsafe_shell=False`` this argument
1866            dictates whether ``~`` is expanded in paths and environment variables
1867            are expanded before running the command. When ``True`` a string such as
1868            ``$SHELL`` will be expanded regardless of escaping. When ``False`` and
1869            ``use_unsafe_shell=False`` no path or variable expansion will be done.
1870        :kw pass_fds: When running on Python 3 this argument
1871            dictates which file descriptors should be passed
1872            to an underlying ``Popen`` constructor. On Python 2, this will
1873            set ``close_fds`` to False.
1874        :kw before_communicate_callback: This function will be called
1875            after ``Popen`` object will be created
1876            but before communicating to the process.
1877            (``Popen`` object will be passed to callback as a first argument)
1878        :kw ignore_invalid_cwd: This flag indicates whether an invalid ``cwd``
1879            (non-existent or not a directory) should be ignored or should raise
1880            an exception.
1881        :returns: A 3-tuple of return code (integer), stdout (native string),
1882            and stderr (native string).  On python2, stdout and stderr are both
1883            byte strings.  On python3, stdout and stderr are text strings converted
1884            according to the encoding and errors parameters.  If you want byte
1885            strings on python3, use encoding=None to turn decoding to text off.
1886        '''
1887        # used by clean args later on
1888        self._clean = None
1889
1890        if not isinstance(args, (list, binary_type, text_type)):
1891            msg = "Argument 'args' to run_command must be list or string"
1892            self.fail_json(rc=257, cmd=args, msg=msg)
1893
1894        shell = False
1895        if use_unsafe_shell:
1896
1897            # stringify args for unsafe/direct shell usage
1898            if isinstance(args, list):
1899                args = b" ".join([to_bytes(shlex_quote(x), errors='surrogate_or_strict') for x in args])
1900            else:
1901                args = to_bytes(args, errors='surrogate_or_strict')
1902
1903            # not set explicitly, check if set by controller
1904            if executable:
1905                executable = to_bytes(executable, errors='surrogate_or_strict')
1906                args = [executable, b'-c', args]
1907            elif self._shell not in (None, '/bin/sh'):
1908                args = [to_bytes(self._shell, errors='surrogate_or_strict'), b'-c', args]
1909            else:
1910                shell = True
1911        else:
1912            # ensure args are a list
1913            if isinstance(args, (binary_type, text_type)):
1914                # On python2.6 and below, shlex has problems with text type
1915                # On python3, shlex needs a text type.
1916                if PY2:
1917                    args = to_bytes(args, errors='surrogate_or_strict')
1918                elif PY3:
1919                    args = to_text(args, errors='surrogateescape')
1920                args = shlex.split(args)
1921
1922            # expand ``~`` in paths, and all environment vars
1923            if expand_user_and_vars:
1924                args = [to_bytes(os.path.expanduser(os.path.expandvars(x)), errors='surrogate_or_strict') for x in args if x is not None]
1925            else:
1926                args = [to_bytes(x, errors='surrogate_or_strict') for x in args if x is not None]
1927
1928        prompt_re = None
1929        if prompt_regex:
1930            if isinstance(prompt_regex, text_type):
1931                if PY3:
1932                    prompt_regex = to_bytes(prompt_regex, errors='surrogateescape')
1933                elif PY2:
1934                    prompt_regex = to_bytes(prompt_regex, errors='surrogate_or_strict')
1935            try:
1936                prompt_re = re.compile(prompt_regex, re.MULTILINE)
1937            except re.error:
1938                self.fail_json(msg="invalid prompt regular expression given to run_command")
1939
1940        rc = 0
1941        msg = None
1942        st_in = None
1943
1944        # Manipulate the environ we'll send to the new process
1945        old_env_vals = {}
1946        # We can set this from both an attribute and per call
1947        for key, val in self.run_command_environ_update.items():
1948            old_env_vals[key] = os.environ.get(key, None)
1949            os.environ[key] = val
1950        if environ_update:
1951            for key, val in environ_update.items():
1952                old_env_vals[key] = os.environ.get(key, None)
1953                os.environ[key] = val
1954        if path_prefix:
1955            path = os.environ.get('PATH', '')
1956            old_env_vals['PATH'] = path
1957            if path:
1958                os.environ['PATH'] = "%s:%s" % (path_prefix, path)
1959            else:
1960                os.environ['PATH'] = path_prefix
1961
1962        # If using test-module.py and explode, the remote lib path will resemble:
1963        #   /tmp/test_module_scratch/debug_dir/ansible/module_utils/basic.py
1964        # If using ansible or ansible-playbook with a remote system:
1965        #   /tmp/ansible_vmweLQ/ansible_modlib.zip/ansible/module_utils/basic.py
1966
1967        # Clean out python paths set by ansiballz
1968        if 'PYTHONPATH' in os.environ:
1969            pypaths = os.environ['PYTHONPATH'].split(':')
1970            pypaths = [x for x in pypaths
1971                       if not x.endswith('/ansible_modlib.zip') and
1972                       not x.endswith('/debug_dir')]
1973            os.environ['PYTHONPATH'] = ':'.join(pypaths)
1974            if not os.environ['PYTHONPATH']:
1975                del os.environ['PYTHONPATH']
1976
1977        if data:
1978            st_in = subprocess.PIPE
1979
1980        kwargs = dict(
1981            executable=executable,
1982            shell=shell,
1983            close_fds=close_fds,
1984            stdin=st_in,
1985            stdout=subprocess.PIPE,
1986            stderr=subprocess.PIPE,
1987            preexec_fn=self._restore_signal_handlers,
1988        )
1989        if PY3 and pass_fds:
1990            kwargs["pass_fds"] = pass_fds
1991        elif PY2 and pass_fds:
1992            kwargs['close_fds'] = False
1993
1994        # store the pwd
1995        prev_dir = os.getcwd()
1996
1997        # make sure we're in the right working directory
1998        if cwd:
1999            if os.path.isdir(cwd):
2000                cwd = to_bytes(os.path.abspath(os.path.expanduser(cwd)), errors='surrogate_or_strict')
2001                kwargs['cwd'] = cwd
2002                try:
2003                    os.chdir(cwd)
2004                except (OSError, IOError) as e:
2005                    self.fail_json(rc=e.errno, msg="Could not chdir to %s, %s" % (cwd, to_native(e)),
2006                                   exception=traceback.format_exc())
2007            elif not ignore_invalid_cwd:
2008                self.fail_json(msg="Provided cwd is not a valid directory: %s" % cwd)
2009
2010        old_umask = None
2011        if umask:
2012            old_umask = os.umask(umask)
2013
2014        try:
2015            if self._debug:
2016                self.log('Executing: ' + self._clean_args(args))
2017            cmd = subprocess.Popen(args, **kwargs)
2018            if before_communicate_callback:
2019                before_communicate_callback(cmd)
2020
2021            # the communication logic here is essentially taken from that
2022            # of the _communicate() function in ssh.py
2023
2024            stdout = b''
2025            stderr = b''
2026            try:
2027                selector = selectors.DefaultSelector()
2028            except (IOError, OSError):
2029                # Failed to detect default selector for the given platform
2030                # Select PollSelector which is supported by major platforms
2031                selector = selectors.PollSelector()
2032
2033            selector.register(cmd.stdout, selectors.EVENT_READ)
2034            selector.register(cmd.stderr, selectors.EVENT_READ)
2035            if os.name == 'posix':
2036                fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_SETFL, fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
2037                fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_SETFL, fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
2038
2039            if data:
2040                if not binary_data:
2041                    data += '\n'
2042                if isinstance(data, text_type):
2043                    data = to_bytes(data)
2044                cmd.stdin.write(data)
2045                cmd.stdin.close()
2046
2047            while True:
2048                events = selector.select(1)
2049                for key, event in events:
2050                    b_chunk = key.fileobj.read()
2051                    if b_chunk == b(''):
2052                        selector.unregister(key.fileobj)
2053                    if key.fileobj == cmd.stdout:
2054                        stdout += b_chunk
2055                    elif key.fileobj == cmd.stderr:
2056                        stderr += b_chunk
2057                # if we're checking for prompts, do it now
2058                if prompt_re:
2059                    if prompt_re.search(stdout) and not data:
2060                        if encoding:
2061                            stdout = to_native(stdout, encoding=encoding, errors=errors)
2062                        return (257, stdout, "A prompt was encountered while running a command, but no input data was specified")
2063                # only break out if no pipes are left to read or
2064                # the pipes are completely read and
2065                # the process is terminated
2066                if (not events or not selector.get_map()) and cmd.poll() is not None:
2067                    break
2068                # No pipes are left to read but process is not yet terminated
2069                # Only then it is safe to wait for the process to be finished
2070                # NOTE: Actually cmd.poll() is always None here if no selectors are left
2071                elif not selector.get_map() and cmd.poll() is None:
2072                    cmd.wait()
2073                    # The process is terminated. Since no pipes to read from are
2074                    # left, there is no need to call select() again.
2075                    break
2076
2077            cmd.stdout.close()
2078            cmd.stderr.close()
2079            selector.close()
2080
2081            rc = cmd.returncode
2082        except (OSError, IOError) as e:
2083            self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(e)))
2084            self.fail_json(rc=e.errno, stdout=b'', stderr=b'', msg=to_native(e), cmd=self._clean_args(args))
2085        except Exception as e:
2086            self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(traceback.format_exc())))
2087            self.fail_json(rc=257, stdout=b'', stderr=b'', msg=to_native(e), exception=traceback.format_exc(), cmd=self._clean_args(args))
2088
2089        # Restore env settings
2090        for key, val in old_env_vals.items():
2091            if val is None:
2092                del os.environ[key]
2093            else:
2094                os.environ[key] = val
2095
2096        if old_umask:
2097            os.umask(old_umask)
2098
2099        if rc != 0 and check_rc:
2100            msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values)
2101            self.fail_json(cmd=self._clean_args(args), rc=rc, stdout=stdout, stderr=stderr, msg=msg)
2102
2103        # reset the pwd
2104        os.chdir(prev_dir)
2105
2106        if encoding is not None:
2107            return (rc, to_native(stdout, encoding=encoding, errors=errors),
2108                    to_native(stderr, encoding=encoding, errors=errors))
2109
2110        return (rc, stdout, stderr)
2111
2112    def append_to_file(self, filename, str):
2113        filename = os.path.expandvars(os.path.expanduser(filename))
2114        fh = open(filename, 'a')
2115        fh.write(str)
2116        fh.close()
2117
2118    def bytes_to_human(self, size):
2119        return bytes_to_human(size)
2120
2121    # for backwards compatibility
2122    pretty_bytes = bytes_to_human
2123
2124    def human_to_bytes(self, number, isbits=False):
2125        return human_to_bytes(number, isbits)
2126
2127    #
2128    # Backwards compat
2129    #
2130
2131    # In 2.0, moved from inside the module to the toplevel
2132    is_executable = is_executable
2133
2134    @staticmethod
2135    def get_buffer_size(fd):
2136        try:
2137            # 1032 == FZ_GETPIPE_SZ
2138            buffer_size = fcntl.fcntl(fd, 1032)
2139        except Exception:
2140            try:
2141                # not as exact as above, but should be good enough for most platforms that fail the previous call
2142                buffer_size = select.PIPE_BUF
2143            except Exception:
2144                buffer_size = 9000  # use sane default JIC
2145
2146        return buffer_size
2147
2148
2149def get_module_path():
2150    return os.path.dirname(os.path.realpath(__file__))
2151