1# Copyright (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
2# Copyright 2015 Abhijit Menon-Sen <ams@2ndQuadrant.com>
3# Copyright 2017 Toshio Kuratomi <tkuratomi@ansible.com>
4# Copyright (c) 2017 Ansible Project
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import (absolute_import, division, print_function)
8__metaclass__ = type
9
10DOCUMENTATION = '''
11    connection: ssh
12    short_description: connect via ssh client binary
13    description:
14        - This connection plugin allows ansible to communicate to the target machines via normal ssh command line.
15        - Ansible does not expose a channel to allow communication between the user and the ssh process to accept
16          a password manually to decrypt an ssh key when using this connection plugin (which is the default). The
17          use of ``ssh-agent`` is highly recommended.
18    author: ansible (@core)
19    version_added: historical
20    options:
21      host:
22          description: Hostname/ip to connect to.
23          default: inventory_hostname
24          vars:
25               - name: ansible_host
26               - name: ansible_ssh_host
27      host_key_checking:
28          description: Determines if ssh should check host keys
29          type: boolean
30          ini:
31              - section: defaults
32                key: 'host_key_checking'
33              - section: ssh_connection
34                key: 'host_key_checking'
35                version_added: '2.5'
36          env:
37              - name: ANSIBLE_HOST_KEY_CHECKING
38              - name: ANSIBLE_SSH_HOST_KEY_CHECKING
39                version_added: '2.5'
40          vars:
41              - name: ansible_host_key_checking
42                version_added: '2.5'
43              - name: ansible_ssh_host_key_checking
44                version_added: '2.5'
45      password:
46          description: Authentication password for the C(remote_user). Can be supplied as CLI option.
47          vars:
48              - name: ansible_password
49              - name: ansible_ssh_pass
50              - name: ansible_ssh_password
51      ssh_args:
52          description: Arguments to pass to all ssh cli tools
53          default: '-C -o ControlMaster=auto -o ControlPersist=60s'
54          ini:
55              - section: 'ssh_connection'
56                key: 'ssh_args'
57          env:
58              - name: ANSIBLE_SSH_ARGS
59          vars:
60              - name: ansible_ssh_args
61                version_added: '2.7'
62      ssh_common_args:
63          description: Common extra args for all ssh CLI tools
64          ini:
65              - section: 'ssh_connection'
66                key: 'ssh_common_args'
67                version_added: '2.7'
68          env:
69              - name: ANSIBLE_SSH_COMMON_ARGS
70                version_added: '2.7'
71          vars:
72              - name: ansible_ssh_common_args
73      ssh_executable:
74          default: ssh
75          description:
76            - This defines the location of the ssh binary. It defaults to ``ssh`` which will use the first ssh binary available in $PATH.
77            - This option is usually not required, it might be useful when access to system ssh is restricted,
78              or when using ssh wrappers to connect to remote hosts.
79          env: [{name: ANSIBLE_SSH_EXECUTABLE}]
80          ini:
81          - {key: ssh_executable, section: ssh_connection}
82          #const: ANSIBLE_SSH_EXECUTABLE
83          version_added: "2.2"
84          vars:
85              - name: ansible_ssh_executable
86                version_added: '2.7'
87      sftp_executable:
88          default: sftp
89          description:
90            - This defines the location of the sftp binary. It defaults to ``sftp`` which will use the first binary available in $PATH.
91          env: [{name: ANSIBLE_SFTP_EXECUTABLE}]
92          ini:
93          - {key: sftp_executable, section: ssh_connection}
94          version_added: "2.6"
95          vars:
96              - name: ansible_sftp_executable
97                version_added: '2.7'
98      scp_executable:
99          default: scp
100          description:
101            - This defines the location of the scp binary. It defaults to `scp` which will use the first binary available in $PATH.
102          env: [{name: ANSIBLE_SCP_EXECUTABLE}]
103          ini:
104          - {key: scp_executable, section: ssh_connection}
105          version_added: "2.6"
106          vars:
107              - name: ansible_scp_executable
108                version_added: '2.7'
109      scp_extra_args:
110          description: Extra exclusive to the ``scp`` CLI
111          vars:
112              - name: ansible_scp_extra_args
113          env:
114            - name: ANSIBLE_SCP_EXTRA_ARGS
115              version_added: '2.7'
116          ini:
117            - key: scp_extra_args
118              section: ssh_connection
119              version_added: '2.7'
120      sftp_extra_args:
121          description: Extra exclusive to the ``sftp`` CLI
122          vars:
123              - name: ansible_sftp_extra_args
124          env:
125            - name: ANSIBLE_SFTP_EXTRA_ARGS
126              version_added: '2.7'
127          ini:
128            - key: sftp_extra_args
129              section: ssh_connection
130              version_added: '2.7'
131      ssh_extra_args:
132          description: Extra exclusive to the 'ssh' CLI
133          vars:
134              - name: ansible_ssh_extra_args
135          env:
136            - name: ANSIBLE_SSH_EXTRA_ARGS
137              version_added: '2.7'
138          ini:
139            - key: ssh_extra_args
140              section: ssh_connection
141              version_added: '2.7'
142      retries:
143          # constant: ANSIBLE_SSH_RETRIES
144          description: Number of attempts to connect.
145          default: 3
146          type: integer
147          env:
148            - name: ANSIBLE_SSH_RETRIES
149          ini:
150            - section: connection
151              key: retries
152            - section: ssh_connection
153              key: retries
154          vars:
155            - name: ansible_ssh_retries
156              version_added: '2.7'
157      port:
158          description: Remote port to connect to.
159          type: int
160          default: 22
161          ini:
162            - section: defaults
163              key: remote_port
164          env:
165            - name: ANSIBLE_REMOTE_PORT
166          vars:
167            - name: ansible_port
168            - name: ansible_ssh_port
169      remote_user:
170          description:
171              - User name with which to login to the remote server, normally set by the remote_user keyword.
172              - If no user is supplied, Ansible will let the ssh client binary choose the user as it normally
173          ini:
174            - section: defaults
175              key: remote_user
176          env:
177            - name: ANSIBLE_REMOTE_USER
178          vars:
179            - name: ansible_user
180            - name: ansible_ssh_user
181      pipelining:
182          default: ANSIBLE_PIPELINING
183          description:
184            - Pipelining reduces the number of SSH operations required to execute a module on the remote server,
185              by executing many Ansible modules without actual file transfer.
186            - This can result in a very significant performance improvement when enabled.
187            - However this conflicts with privilege escalation (become).
188              For example, when using sudo operations you must first disable 'requiretty' in the sudoers file for the target hosts,
189              which is why this feature is disabled by default.
190          env:
191            - name: ANSIBLE_PIPELINING
192            #- name: ANSIBLE_SSH_PIPELINING
193          ini:
194            - section: defaults
195              key: pipelining
196            #- section: ssh_connection
197            #  key: pipelining
198          type: boolean
199          vars:
200            - name: ansible_pipelining
201            - name: ansible_ssh_pipelining
202      private_key_file:
203          description:
204              - Path to private key file to use for authentication
205          ini:
206            - section: defaults
207              key: private_key_file
208          env:
209            - name: ANSIBLE_PRIVATE_KEY_FILE
210          vars:
211            - name: ansible_private_key_file
212            - name: ansible_ssh_private_key_file
213
214      control_path:
215        description:
216          - This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution.
217          - Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting.
218        env:
219          - name: ANSIBLE_SSH_CONTROL_PATH
220        ini:
221          - key: control_path
222            section: ssh_connection
223        vars:
224          - name: ansible_control_path
225            version_added: '2.7'
226      control_path_dir:
227        default: ~/.ansible/cp
228        description:
229          - This sets the directory to use for ssh control path if the control path setting is null.
230          - Also, provides the `%(directory)s` variable for the control path setting.
231        env:
232          - name: ANSIBLE_SSH_CONTROL_PATH_DIR
233        ini:
234          - section: ssh_connection
235            key: control_path_dir
236        vars:
237          - name: ansible_control_path_dir
238            version_added: '2.7'
239      sftp_batch_mode:
240        default: 'yes'
241        description: 'TODO: write it'
242        env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
243        ini:
244        - {key: sftp_batch_mode, section: ssh_connection}
245        type: bool
246        vars:
247          - name: ansible_sftp_batch_mode
248            version_added: '2.7'
249      scp_if_ssh:
250        default: smart
251        description:
252          - "Prefered method to use when transfering files over ssh"
253          - When set to smart, Ansible will try them until one succeeds or they all fail
254          - If set to True, it will force 'scp', if False it will use 'sftp'
255        env: [{name: ANSIBLE_SCP_IF_SSH}]
256        ini:
257        - {key: scp_if_ssh, section: ssh_connection}
258        vars:
259          - name: ansible_scp_if_ssh
260            version_added: '2.7'
261      use_tty:
262        version_added: '2.5'
263        default: 'yes'
264        description: add -tt to ssh commands to force tty allocation
265        env: [{name: ANSIBLE_SSH_USETTY}]
266        ini:
267        - {key: usetty, section: ssh_connection}
268        type: bool
269        vars:
270          - name: ansible_ssh_use_tty
271            version_added: '2.7'
272'''
273
274import errno
275import fcntl
276import hashlib
277import os
278import pty
279import re
280import subprocess
281import time
282
283from functools import wraps
284from ansible import constants as C
285from ansible.errors import (
286    AnsibleAuthenticationFailure,
287    AnsibleConnectionFailure,
288    AnsibleError,
289    AnsibleFileNotFound,
290)
291from ansible.errors import AnsibleOptionsError
292from ansible.module_utils.compat import selectors
293from ansible.module_utils.six import PY3, text_type, binary_type
294from ansible.module_utils.six.moves import shlex_quote
295from ansible.module_utils._text import to_bytes, to_native, to_text
296from ansible.module_utils.parsing.convert_bool import BOOLEANS, boolean
297from ansible.plugins.connection import ConnectionBase, BUFSIZE
298from ansible.plugins.shell.powershell import _parse_clixml
299from ansible.utils.display import Display
300from ansible.utils.path import unfrackpath, makedirs_safe
301
302display = Display()
303
304
305b_NOT_SSH_ERRORS = (b'Traceback (most recent call last):',  # Python-2.6 when there's an exception
306                                                            # while invoking a script via -m
307                    b'PHP Parse error:',  # Php always returns error 255
308                    )
309
310SSHPASS_AVAILABLE = None
311
312
313class AnsibleControlPersistBrokenPipeError(AnsibleError):
314    ''' ControlPersist broken pipe '''
315    pass
316
317
318def _handle_error(remaining_retries, command, return_tuple, no_log, host, display=display):
319
320    # sshpass errors
321    if command == b'sshpass':
322        # Error 5 is invalid/incorrect password. Raise an exception to prevent retries from locking the account.
323        if return_tuple[0] == 5:
324            msg = 'Invalid/incorrect username/password. Skipping remaining {0} retries to prevent account lockout:'.format(remaining_retries)
325            if remaining_retries <= 0:
326                msg = 'Invalid/incorrect password:'
327            if no_log:
328                msg = '{0} <error censored due to no log>'.format(msg)
329            else:
330                msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
331            raise AnsibleAuthenticationFailure(msg)
332
333        # sshpass returns codes are 1-6. We handle 5 previously, so this catches other scenarios.
334        # No exception is raised, so the connection is retried.
335        elif return_tuple[0] in [1, 2, 3, 4, 6]:
336            msg = 'sshpass error:'
337            if no_log:
338                msg = '{0} <error censored due to no log>'.format(msg)
339            else:
340                msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
341
342    if return_tuple[0] == 255:
343        SSH_ERROR = True
344        for signature in b_NOT_SSH_ERRORS:
345            if signature in return_tuple[1]:
346                SSH_ERROR = False
347                break
348
349        if SSH_ERROR:
350            msg = "Failed to connect to the host via ssh:"
351            if no_log:
352                msg = '{0} <error censored due to no log>'.format(msg)
353            else:
354                msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
355            raise AnsibleConnectionFailure(msg)
356
357    # For other errors, no execption is raised so the connection is retried and we only log the messages
358    if 1 <= return_tuple[0] <= 254:
359        msg = u"Failed to connect to the host via ssh:"
360        if no_log:
361            msg = u'{0} <error censored due to no log>'.format(msg)
362        else:
363            msg = u'{0} {1}'.format(msg, to_text(return_tuple[2]).rstrip())
364        display.vvv(msg, host=host)
365
366
367def _ssh_retry(func):
368    """
369    Decorator to retry ssh/scp/sftp in the case of a connection failure
370
371    Will retry if:
372    * an exception is caught
373    * ssh returns 255
374    Will not retry if
375    * sshpass returns 5 (invalid password, to prevent account lockouts)
376    * remaining_tries is < 2
377    * retries limit reached
378    """
379    @wraps(func)
380    def wrapped(self, *args, **kwargs):
381        remaining_tries = int(C.ANSIBLE_SSH_RETRIES) + 1
382        cmd_summary = u"%s..." % to_text(args[0])
383        for attempt in range(remaining_tries):
384            cmd = args[0]
385            if attempt != 0 and self._play_context.password and isinstance(cmd, list):
386                # If this is a retry, the fd/pipe for sshpass is closed, and we need a new one
387                self.sshpass_pipe = os.pipe()
388                cmd[1] = b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')
389
390            try:
391                try:
392                    return_tuple = func(self, *args, **kwargs)
393                    if self._play_context.no_log:
394                        display.vvv(u'rc=%s, stdout and stderr censored due to no log' % return_tuple[0], host=self.host)
395                    else:
396                        display.vvv(return_tuple, host=self.host)
397                    # 0 = success
398                    # 1-254 = remote command return code
399                    # 255 could be a failure from the ssh command itself
400                except (AnsibleControlPersistBrokenPipeError):
401                    # Retry one more time because of the ControlPersist broken pipe (see #16731)
402                    cmd = args[0]
403                    if self._play_context.password and isinstance(cmd, list):
404                        # This is a retry, so the fd/pipe for sshpass is closed, and we need a new one
405                        self.sshpass_pipe = os.pipe()
406                        cmd[1] = b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')
407                    display.vvv(u"RETRYING BECAUSE OF CONTROLPERSIST BROKEN PIPE")
408                    return_tuple = func(self, *args, **kwargs)
409
410                remaining_retries = remaining_tries - attempt - 1
411                _handle_error(remaining_retries, cmd[0], return_tuple, self._play_context.no_log, self.host)
412
413                break
414
415            # 5 = Invalid/incorrect password from sshpass
416            except AnsibleAuthenticationFailure:
417                # Raising this exception, which is subclassed from AnsibleConnectionFailure, prevents further retries
418                raise
419
420            except (AnsibleConnectionFailure, Exception) as e:
421
422                if attempt == remaining_tries - 1:
423                    raise
424                else:
425                    pause = 2 ** attempt - 1
426                    if pause > 30:
427                        pause = 30
428
429                    if isinstance(e, AnsibleConnectionFailure):
430                        msg = u"ssh_retry: attempt: %d, ssh return code is 255. cmd (%s), pausing for %d seconds" % (attempt + 1, cmd_summary, pause)
431                    else:
432                        msg = (u"ssh_retry: attempt: %d, caught exception(%s) from cmd (%s), "
433                               u"pausing for %d seconds" % (attempt + 1, to_text(e), cmd_summary, pause))
434
435                    display.vv(msg, host=self.host)
436
437                    time.sleep(pause)
438                    continue
439
440        return return_tuple
441    return wrapped
442
443
444class Connection(ConnectionBase):
445    ''' ssh based connections '''
446
447    transport = 'ssh'
448    has_pipelining = True
449
450    def __init__(self, *args, **kwargs):
451        super(Connection, self).__init__(*args, **kwargs)
452
453        self.host = self._play_context.remote_addr
454        self.port = self._play_context.port
455        self.user = self._play_context.remote_user
456        self.control_path = C.ANSIBLE_SSH_CONTROL_PATH
457        self.control_path_dir = C.ANSIBLE_SSH_CONTROL_PATH_DIR
458
459        # Windows operates differently from a POSIX connection/shell plugin,
460        # we need to set various properties to ensure SSH on Windows continues
461        # to work
462        if getattr(self._shell, "_IS_WINDOWS", False):
463            self.has_native_async = True
464            self.always_pipeline_modules = True
465            self.module_implementation_preferences = ('.ps1', '.exe', '')
466            self.allow_executable = False
467
468    # The connection is created by running ssh/scp/sftp from the exec_command,
469    # put_file, and fetch_file methods, so we don't need to do any connection
470    # management here.
471
472    def _connect(self):
473        return self
474
475    @staticmethod
476    def _create_control_path(host, port, user, connection=None, pid=None):
477        '''Make a hash for the controlpath based on con attributes'''
478        pstring = '%s-%s-%s' % (host, port, user)
479        if connection:
480            pstring += '-%s' % connection
481        if pid:
482            pstring += '-%s' % to_text(pid)
483        m = hashlib.sha1()
484        m.update(to_bytes(pstring))
485        digest = m.hexdigest()
486        cpath = '%(directory)s/' + digest[:10]
487        return cpath
488
489    @staticmethod
490    def _sshpass_available():
491        global SSHPASS_AVAILABLE
492
493        # We test once if sshpass is available, and remember the result. It
494        # would be nice to use distutils.spawn.find_executable for this, but
495        # distutils isn't always available; shutils.which() is Python3-only.
496
497        if SSHPASS_AVAILABLE is None:
498            try:
499                p = subprocess.Popen(["sshpass"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
500                p.communicate()
501                SSHPASS_AVAILABLE = True
502            except OSError:
503                SSHPASS_AVAILABLE = False
504
505        return SSHPASS_AVAILABLE
506
507    @staticmethod
508    def _persistence_controls(b_command):
509        '''
510        Takes a command array and scans it for ControlPersist and ControlPath
511        settings and returns two booleans indicating whether either was found.
512        This could be smarter, e.g. returning false if ControlPersist is 'no',
513        but for now we do it simple way.
514        '''
515
516        controlpersist = False
517        controlpath = False
518
519        for b_arg in (a.lower() for a in b_command):
520            if b'controlpersist' in b_arg:
521                controlpersist = True
522            elif b'controlpath' in b_arg:
523                controlpath = True
524
525        return controlpersist, controlpath
526
527    def _add_args(self, b_command, b_args, explanation):
528        """
529        Adds arguments to the ssh command and displays a caller-supplied explanation of why.
530
531        :arg b_command: A list containing the command to add the new arguments to.
532            This list will be modified by this method.
533        :arg b_args: An iterable of new arguments to add.  This iterable is used
534            more than once so it must be persistent (ie: a list is okay but a
535            StringIO would not)
536        :arg explanation: A text string containing explaining why the arguments
537            were added.  It will be displayed with a high enough verbosity.
538        .. note:: This function does its work via side-effect.  The b_command list has the new arguments appended.
539        """
540        display.vvvvv(u'SSH: %s: (%s)' % (explanation, ')('.join(to_text(a) for a in b_args)), host=self._play_context.remote_addr)
541        b_command += b_args
542
543    def _build_command(self, binary, *other_args):
544        '''
545        Takes a binary (ssh, scp, sftp) and optional extra arguments and returns
546        a command line as an array that can be passed to subprocess.Popen.
547        '''
548
549        b_command = []
550
551        #
552        # First, the command to invoke
553        #
554
555        # If we want to use password authentication, we have to set up a pipe to
556        # write the password to sshpass.
557
558        if self._play_context.password:
559            if not self._sshpass_available():
560                raise AnsibleError("to use the 'ssh' connection type with passwords, you must install the sshpass program")
561
562            self.sshpass_pipe = os.pipe()
563            b_command += [b'sshpass', b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')]
564
565        if binary == 'ssh':
566            b_command += [to_bytes(self._play_context.ssh_executable, errors='surrogate_or_strict')]
567        else:
568            b_command += [to_bytes(binary, errors='surrogate_or_strict')]
569
570        #
571        # Next, additional arguments based on the configuration.
572        #
573
574        # sftp batch mode allows us to correctly catch failed transfers, but can
575        # be disabled if the client side doesn't support the option. However,
576        # sftp batch mode does not prompt for passwords so it must be disabled
577        # if not using controlpersist and using sshpass
578        if binary == 'sftp' and C.DEFAULT_SFTP_BATCH_MODE:
579            if self._play_context.password:
580                b_args = [b'-o', b'BatchMode=no']
581                self._add_args(b_command, b_args, u'disable batch mode for sshpass')
582            b_command += [b'-b', b'-']
583
584        if self._play_context.verbosity > 3:
585            b_command.append(b'-vvv')
586
587        #
588        # Next, we add [ssh_connection]ssh_args from ansible.cfg.
589        #
590
591        if self._play_context.ssh_args:
592            b_args = [to_bytes(a, errors='surrogate_or_strict') for a in
593                      self._split_ssh_args(self._play_context.ssh_args)]
594            self._add_args(b_command, b_args, u"ansible.cfg set ssh_args")
595
596        # Now we add various arguments controlled by configuration file settings
597        # (e.g. host_key_checking) or inventory variables (ansible_ssh_port) or
598        # a combination thereof.
599
600        if not C.HOST_KEY_CHECKING:
601            b_args = (b"-o", b"StrictHostKeyChecking=no")
602            self._add_args(b_command, b_args, u"ANSIBLE_HOST_KEY_CHECKING/host_key_checking disabled")
603
604        if self._play_context.port is not None:
605            b_args = (b"-o", b"Port=" + to_bytes(self._play_context.port, nonstring='simplerepr', errors='surrogate_or_strict'))
606            self._add_args(b_command, b_args, u"ANSIBLE_REMOTE_PORT/remote_port/ansible_port set")
607
608        key = self._play_context.private_key_file
609        if key:
610            b_args = (b"-o", b'IdentityFile="' + to_bytes(os.path.expanduser(key), errors='surrogate_or_strict') + b'"')
611            self._add_args(b_command, b_args, u"ANSIBLE_PRIVATE_KEY_FILE/private_key_file/ansible_ssh_private_key_file set")
612
613        if not self._play_context.password:
614            self._add_args(
615                b_command, (
616                    b"-o", b"KbdInteractiveAuthentication=no",
617                    b"-o", b"PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey",
618                    b"-o", b"PasswordAuthentication=no"
619                ),
620                u"ansible_password/ansible_ssh_password not set"
621            )
622
623        user = self._play_context.remote_user
624        if user:
625            self._add_args(
626                b_command,
627                (b"-o", b'User="%s"' % to_bytes(self._play_context.remote_user, errors='surrogate_or_strict')),
628                u"ANSIBLE_REMOTE_USER/remote_user/ansible_user/user/-u set"
629            )
630
631        self._add_args(
632            b_command,
633            (b"-o", b"ConnectTimeout=" + to_bytes(self._play_context.timeout, errors='surrogate_or_strict', nonstring='simplerepr')),
634            u"ANSIBLE_TIMEOUT/timeout set"
635        )
636
637        # Add in any common or binary-specific arguments from the PlayContext
638        # (i.e. inventory or task settings or overrides on the command line).
639
640        for opt in (u'ssh_common_args', u'{0}_extra_args'.format(binary)):
641            attr = getattr(self._play_context, opt, None)
642            if attr is not None:
643                b_args = [to_bytes(a, errors='surrogate_or_strict') for a in self._split_ssh_args(attr)]
644                self._add_args(b_command, b_args, u"PlayContext set %s" % opt)
645
646        # Check if ControlPersist is enabled and add a ControlPath if one hasn't
647        # already been set.
648
649        controlpersist, controlpath = self._persistence_controls(b_command)
650
651        if controlpersist:
652            self._persistent = True
653
654            if not controlpath:
655                cpdir = unfrackpath(self.control_path_dir)
656                b_cpdir = to_bytes(cpdir, errors='surrogate_or_strict')
657
658                # The directory must exist and be writable.
659                makedirs_safe(b_cpdir, 0o700)
660                if not os.access(b_cpdir, os.W_OK):
661                    raise AnsibleError("Cannot write to ControlPath %s" % to_native(cpdir))
662
663                if not self.control_path:
664                    self.control_path = self._create_control_path(
665                        self.host,
666                        self.port,
667                        self.user
668                    )
669                b_args = (b"-o", b"ControlPath=" + to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict'))
670                self._add_args(b_command, b_args, u"found only ControlPersist; added ControlPath")
671
672        # Finally, we add any caller-supplied extras.
673        if other_args:
674            b_command += [to_bytes(a) for a in other_args]
675
676        return b_command
677
678    def _send_initial_data(self, fh, in_data, ssh_process):
679        '''
680        Writes initial data to the stdin filehandle of the subprocess and closes
681        it. (The handle must be closed; otherwise, for example, "sftp -b -" will
682        just hang forever waiting for more commands.)
683        '''
684
685        display.debug(u'Sending initial data')
686
687        try:
688            fh.write(to_bytes(in_data))
689            fh.close()
690        except (OSError, IOError) as e:
691            # The ssh connection may have already terminated at this point, with a more useful error
692            # Only raise AnsibleConnectionFailure if the ssh process is still alive
693            time.sleep(0.001)
694            ssh_process.poll()
695            if getattr(ssh_process, 'returncode', None) is None:
696                raise AnsibleConnectionFailure(
697                    'Data could not be sent to remote host "%s". Make sure this host can be reached '
698                    'over ssh: %s' % (self.host, to_native(e)), orig_exc=e
699                )
700
701        display.debug(u'Sent initial data (%d bytes)' % len(in_data))
702
703    # Used by _run() to kill processes on failures
704    @staticmethod
705    def _terminate_process(p):
706        """ Terminate a process, ignoring errors """
707        try:
708            p.terminate()
709        except (OSError, IOError):
710            pass
711
712    # This is separate from _run() because we need to do the same thing for stdout
713    # and stderr.
714    def _examine_output(self, source, state, b_chunk, sudoable):
715        '''
716        Takes a string, extracts complete lines from it, tests to see if they
717        are a prompt, error message, etc., and sets appropriate flags in self.
718        Prompt and success lines are removed.
719
720        Returns the processed (i.e. possibly-edited) output and the unprocessed
721        remainder (to be processed with the next chunk) as strings.
722        '''
723
724        output = []
725        for b_line in b_chunk.splitlines(True):
726            display_line = to_text(b_line).rstrip('\r\n')
727            suppress_output = False
728
729            # display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, display_line))
730            if self.become.expect_prompt() and self.become.check_password_prompt(b_line):
731                display.debug(u"become_prompt: (source=%s, state=%s): '%s'" % (source, state, display_line))
732                self._flags['become_prompt'] = True
733                suppress_output = True
734            elif self.become.success and self.become.check_success(b_line):
735                display.debug(u"become_success: (source=%s, state=%s): '%s'" % (source, state, display_line))
736                self._flags['become_success'] = True
737                suppress_output = True
738            elif sudoable and self.become.check_incorrect_password(b_line):
739                display.debug(u"become_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
740                self._flags['become_error'] = True
741            elif sudoable and self.become.check_missing_password(b_line):
742                display.debug(u"become_nopasswd_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
743                self._flags['become_nopasswd_error'] = True
744
745            if not suppress_output:
746                output.append(b_line)
747
748        # The chunk we read was most likely a series of complete lines, but just
749        # in case the last line was incomplete (and not a prompt, which we would
750        # have removed from the output), we retain it to be processed with the
751        # next chunk.
752
753        remainder = b''
754        if output and not output[-1].endswith(b'\n'):
755            remainder = output[-1]
756            output = output[:-1]
757
758        return b''.join(output), remainder
759
760    def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):
761        '''
762        Starts the command and communicates with it until it ends.
763        '''
764
765        # We don't use _shell.quote as this is run on the controller and independent from the shell plugin chosen
766        display_cmd = u' '.join(shlex_quote(to_text(c)) for c in cmd)
767        display.vvv(u'SSH: EXEC {0}'.format(display_cmd), host=self.host)
768
769        # Start the given command. If we don't need to pipeline data, we can try
770        # to use a pseudo-tty (ssh will have been invoked with -tt). If we are
771        # pipelining data, or can't create a pty, we fall back to using plain
772        # old pipes.
773
774        p = None
775
776        if isinstance(cmd, (text_type, binary_type)):
777            cmd = to_bytes(cmd)
778        else:
779            cmd = list(map(to_bytes, cmd))
780
781        if not in_data:
782            try:
783                # Make sure stdin is a proper pty to avoid tcgetattr errors
784                master, slave = pty.openpty()
785                if PY3 and self._play_context.password:
786                    # pylint: disable=unexpected-keyword-arg
787                    p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe)
788                else:
789                    p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
790                stdin = os.fdopen(master, 'wb', 0)
791                os.close(slave)
792            except (OSError, IOError):
793                p = None
794
795        if not p:
796            if PY3 and self._play_context.password:
797                # pylint: disable=unexpected-keyword-arg
798                p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe)
799            else:
800                p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
801            stdin = p.stdin
802
803        # If we are using SSH password authentication, write the password into
804        # the pipe we opened in _build_command.
805
806        if self._play_context.password:
807            os.close(self.sshpass_pipe[0])
808            try:
809                os.write(self.sshpass_pipe[1], to_bytes(self._play_context.password) + b'\n')
810            except OSError as e:
811                # Ignore broken pipe errors if the sshpass process has exited.
812                if e.errno != errno.EPIPE or p.poll() is None:
813                    raise
814            os.close(self.sshpass_pipe[1])
815
816        #
817        # SSH state machine
818        #
819
820        # Now we read and accumulate output from the running process until it
821        # exits. Depending on the circumstances, we may also need to write an
822        # escalation password and/or pipelined input to the process.
823
824        states = [
825            'awaiting_prompt', 'awaiting_escalation', 'ready_to_send', 'awaiting_exit'
826        ]
827
828        # Are we requesting privilege escalation? Right now, we may be invoked
829        # to execute sftp/scp with sudoable=True, but we can request escalation
830        # only when using ssh. Otherwise we can send initial data straightaway.
831
832        state = states.index('ready_to_send')
833        if to_bytes(self.get_option('ssh_executable')) in cmd and sudoable:
834            prompt = getattr(self.become, 'prompt', None)
835            if prompt:
836                # We're requesting escalation with a password, so we have to
837                # wait for a password prompt.
838                state = states.index('awaiting_prompt')
839                display.debug(u'Initial state: %s: %s' % (states[state], to_text(prompt)))
840            elif self.become and self.become.success:
841                # We're requesting escalation without a password, so we have to
842                # detect success/failure before sending any initial data.
843                state = states.index('awaiting_escalation')
844                display.debug(u'Initial state: %s: %s' % (states[state], to_text(self.become.success)))
845
846        # We store accumulated stdout and stderr output from the process here,
847        # but strip any privilege escalation prompt/confirmation lines first.
848        # Output is accumulated into tmp_*, complete lines are extracted into
849        # an array, then checked and removed or copied to stdout or stderr. We
850        # set any flags based on examining the output in self._flags.
851
852        b_stdout = b_stderr = b''
853        b_tmp_stdout = b_tmp_stderr = b''
854
855        self._flags = dict(
856            become_prompt=False, become_success=False,
857            become_error=False, become_nopasswd_error=False
858        )
859
860        # select timeout should be longer than the connect timeout, otherwise
861        # they will race each other when we can't connect, and the connect
862        # timeout usually fails
863        timeout = 2 + self._play_context.timeout
864        for fd in (p.stdout, p.stderr):
865            fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
866
867        # TODO: bcoca would like to use SelectSelector() when open
868        # filehandles is low, then switch to more efficient ones when higher.
869        # select is faster when filehandles is low.
870        selector = selectors.DefaultSelector()
871        selector.register(p.stdout, selectors.EVENT_READ)
872        selector.register(p.stderr, selectors.EVENT_READ)
873
874        # If we can send initial data without waiting for anything, we do so
875        # before we start polling
876        if states[state] == 'ready_to_send' and in_data:
877            self._send_initial_data(stdin, in_data, p)
878            state += 1
879
880        try:
881            while True:
882                poll = p.poll()
883                events = selector.select(timeout)
884
885                # We pay attention to timeouts only while negotiating a prompt.
886
887                if not events:
888                    # We timed out
889                    if state <= states.index('awaiting_escalation'):
890                        # If the process has already exited, then it's not really a
891                        # timeout; we'll let the normal error handling deal with it.
892                        if poll is not None:
893                            break
894                        self._terminate_process(p)
895                        raise AnsibleError('Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, to_native(b_stdout)))
896
897                # Read whatever output is available on stdout and stderr, and stop
898                # listening to the pipe if it's been closed.
899
900                for key, event in events:
901                    if key.fileobj == p.stdout:
902                        b_chunk = p.stdout.read()
903                        if b_chunk == b'':
904                            # stdout has been closed, stop watching it
905                            selector.unregister(p.stdout)
906                            # When ssh has ControlMaster (+ControlPath/Persist) enabled, the
907                            # first connection goes into the background and we never see EOF
908                            # on stderr. If we see EOF on stdout, lower the select timeout
909                            # to reduce the time wasted selecting on stderr if we observe
910                            # that the process has not yet existed after this EOF. Otherwise
911                            # we may spend a long timeout period waiting for an EOF that is
912                            # not going to arrive until the persisted connection closes.
913                            timeout = 1
914                        b_tmp_stdout += b_chunk
915                        display.debug(u"stdout chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk)))
916                    elif key.fileobj == p.stderr:
917                        b_chunk = p.stderr.read()
918                        if b_chunk == b'':
919                            # stderr has been closed, stop watching it
920                            selector.unregister(p.stderr)
921                        b_tmp_stderr += b_chunk
922                        display.debug("stderr chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk)))
923
924                # We examine the output line-by-line until we have negotiated any
925                # privilege escalation prompt and subsequent success/error message.
926                # Afterwards, we can accumulate output without looking at it.
927
928                if state < states.index('ready_to_send'):
929                    if b_tmp_stdout:
930                        b_output, b_unprocessed = self._examine_output('stdout', states[state], b_tmp_stdout, sudoable)
931                        b_stdout += b_output
932                        b_tmp_stdout = b_unprocessed
933
934                    if b_tmp_stderr:
935                        b_output, b_unprocessed = self._examine_output('stderr', states[state], b_tmp_stderr, sudoable)
936                        b_stderr += b_output
937                        b_tmp_stderr = b_unprocessed
938                else:
939                    b_stdout += b_tmp_stdout
940                    b_stderr += b_tmp_stderr
941                    b_tmp_stdout = b_tmp_stderr = b''
942
943                # If we see a privilege escalation prompt, we send the password.
944                # (If we're expecting a prompt but the escalation succeeds, we
945                # didn't need the password and can carry on regardless.)
946
947                if states[state] == 'awaiting_prompt':
948                    if self._flags['become_prompt']:
949                        display.debug(u'Sending become_password in response to prompt')
950                        become_pass = self.become.get_option('become_pass', playcontext=self._play_context)
951                        stdin.write(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n')
952                        # On python3 stdin is a BufferedWriter, and we don't have a guarantee
953                        # that the write will happen without a flush
954                        stdin.flush()
955                        self._flags['become_prompt'] = False
956                        state += 1
957                    elif self._flags['become_success']:
958                        state += 1
959
960                # We've requested escalation (with or without a password), now we
961                # wait for an error message or a successful escalation.
962
963                if states[state] == 'awaiting_escalation':
964                    if self._flags['become_success']:
965                        display.vvv(u'Escalation succeeded')
966                        self._flags['become_success'] = False
967                        state += 1
968                    elif self._flags['become_error']:
969                        display.vvv(u'Escalation failed')
970                        self._terminate_process(p)
971                        self._flags['become_error'] = False
972                        raise AnsibleError('Incorrect %s password' % self.become.name)
973                    elif self._flags['become_nopasswd_error']:
974                        display.vvv(u'Escalation requires password')
975                        self._terminate_process(p)
976                        self._flags['become_nopasswd_error'] = False
977                        raise AnsibleError('Missing %s password' % self.become.name)
978                    elif self._flags['become_prompt']:
979                        # This shouldn't happen, because we should see the "Sorry,
980                        # try again" message first.
981                        display.vvv(u'Escalation prompt repeated')
982                        self._terminate_process(p)
983                        self._flags['become_prompt'] = False
984                        raise AnsibleError('Incorrect %s password' % self.become.name)
985
986                # Once we're sure that the privilege escalation prompt, if any, has
987                # been dealt with, we can send any initial data and start waiting
988                # for output.
989
990                if states[state] == 'ready_to_send':
991                    if in_data:
992                        self._send_initial_data(stdin, in_data, p)
993                    state += 1
994
995                # Now we're awaiting_exit: has the child process exited? If it has,
996                # and we've read all available output from it, we're done.
997
998                if poll is not None:
999                    if not selector.get_map() or not events:
1000                        break
1001                    # We should not see further writes to the stdout/stderr file
1002                    # descriptors after the process has closed, set the select
1003                    # timeout to gather any last writes we may have missed.
1004                    timeout = 0
1005                    continue
1006
1007                # If the process has not yet exited, but we've already read EOF from
1008                # its stdout and stderr (and thus no longer watching any file
1009                # descriptors), we can just wait for it to exit.
1010
1011                elif not selector.get_map():
1012                    p.wait()
1013                    break
1014
1015                # Otherwise there may still be outstanding data to read.
1016        finally:
1017            selector.close()
1018            # close stdin, stdout, and stderr after process is terminated and
1019            # stdout/stderr are read completely (see also issues #848, #64768).
1020            stdin.close()
1021            p.stdout.close()
1022            p.stderr.close()
1023
1024        if C.HOST_KEY_CHECKING:
1025            if cmd[0] == b"sshpass" and p.returncode == 6:
1026                raise AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support '
1027                                   'this.  Please add this host\'s fingerprint to your known_hosts file to manage this host.')
1028
1029        controlpersisterror = b'Bad configuration option: ControlPersist' in b_stderr or b'unknown configuration option: ControlPersist' in b_stderr
1030        if p.returncode != 0 and controlpersisterror:
1031            raise AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" '
1032                               '(or ssh_args in [ssh_connection] section of the config file) before running again')
1033
1034        # If we find a broken pipe because of ControlPersist timeout expiring (see #16731),
1035        # we raise a special exception so that we can retry a connection.
1036        controlpersist_broken_pipe = b'mux_client_hello_exchange: write packet: Broken pipe' in b_stderr
1037        if p.returncode == 255:
1038
1039            additional = to_native(b_stderr)
1040            if controlpersist_broken_pipe:
1041                raise AnsibleControlPersistBrokenPipeError('Data could not be sent because of ControlPersist broken pipe: %s' % additional)
1042
1043            elif in_data and checkrc:
1044                raise AnsibleConnectionFailure('Data could not be sent to remote host "%s". Make sure this host can be reached over ssh: %s'
1045                                               % (self.host, additional))
1046
1047        return (p.returncode, b_stdout, b_stderr)
1048
1049    @_ssh_retry
1050    def _run(self, cmd, in_data, sudoable=True, checkrc=True):
1051        """Wrapper around _bare_run that retries the connection
1052        """
1053        return self._bare_run(cmd, in_data, sudoable=sudoable, checkrc=checkrc)
1054
1055    @_ssh_retry
1056    def _file_transport_command(self, in_path, out_path, sftp_action):
1057        # scp and sftp require square brackets for IPv6 addresses, but
1058        # accept them for hostnames and IPv4 addresses too.
1059        host = '[%s]' % self.host
1060
1061        smart_methods = ['sftp', 'scp', 'piped']
1062
1063        # Windows does not support dd so we cannot use the piped method
1064        if getattr(self._shell, "_IS_WINDOWS", False):
1065            smart_methods.remove('piped')
1066
1067        # Transfer methods to try
1068        methods = []
1069
1070        # Use the transfer_method option if set, otherwise use scp_if_ssh
1071        ssh_transfer_method = self._play_context.ssh_transfer_method
1072        if ssh_transfer_method is not None:
1073            if not (ssh_transfer_method in ('smart', 'sftp', 'scp', 'piped')):
1074                raise AnsibleOptionsError('transfer_method needs to be one of [smart|sftp|scp|piped]')
1075            if ssh_transfer_method == 'smart':
1076                methods = smart_methods
1077            else:
1078                methods = [ssh_transfer_method]
1079        else:
1080            # since this can be a non-bool now, we need to handle it correctly
1081            scp_if_ssh = C.DEFAULT_SCP_IF_SSH
1082            if not isinstance(scp_if_ssh, bool):
1083                scp_if_ssh = scp_if_ssh.lower()
1084                if scp_if_ssh in BOOLEANS:
1085                    scp_if_ssh = boolean(scp_if_ssh, strict=False)
1086                elif scp_if_ssh != 'smart':
1087                    raise AnsibleOptionsError('scp_if_ssh needs to be one of [smart|True|False]')
1088            if scp_if_ssh == 'smart':
1089                methods = smart_methods
1090            elif scp_if_ssh is True:
1091                methods = ['scp']
1092            else:
1093                methods = ['sftp']
1094
1095        for method in methods:
1096            returncode = stdout = stderr = None
1097            if method == 'sftp':
1098                cmd = self._build_command(self.get_option('sftp_executable'), to_bytes(host))
1099                in_data = u"{0} {1} {2}\n".format(sftp_action, shlex_quote(in_path), shlex_quote(out_path))
1100                in_data = to_bytes(in_data, nonstring='passthru')
1101                (returncode, stdout, stderr) = self._bare_run(cmd, in_data, checkrc=False)
1102            elif method == 'scp':
1103                scp = self.get_option('scp_executable')
1104
1105                if sftp_action == 'get':
1106                    cmd = self._build_command(scp, u'{0}:{1}'.format(host, self._shell.quote(in_path)), out_path)
1107                else:
1108                    cmd = self._build_command(scp, in_path, u'{0}:{1}'.format(host, self._shell.quote(out_path)))
1109                in_data = None
1110                (returncode, stdout, stderr) = self._bare_run(cmd, in_data, checkrc=False)
1111            elif method == 'piped':
1112                if sftp_action == 'get':
1113                    # we pass sudoable=False to disable pty allocation, which
1114                    # would end up mixing stdout/stderr and screwing with newlines
1115                    (returncode, stdout, stderr) = self.exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE), sudoable=False)
1116                    with open(to_bytes(out_path, errors='surrogate_or_strict'), 'wb+') as out_file:
1117                        out_file.write(stdout)
1118                else:
1119                    with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as f:
1120                        in_data = to_bytes(f.read(), nonstring='passthru')
1121                    if not in_data:
1122                        count = ' count=0'
1123                    else:
1124                        count = ''
1125                    (returncode, stdout, stderr) = self.exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), in_data=in_data, sudoable=False)
1126
1127            # Check the return code and rollover to next method if failed
1128            if returncode == 0:
1129                return (returncode, stdout, stderr)
1130            else:
1131                # If not in smart mode, the data will be printed by the raise below
1132                if len(methods) > 1:
1133                    display.warning(u'%s transfer mechanism failed on %s. Use ANSIBLE_DEBUG=1 to see detailed information' % (method, host))
1134                    display.debug(u'%s' % to_text(stdout))
1135                    display.debug(u'%s' % to_text(stderr))
1136
1137        if returncode == 255:
1138            raise AnsibleConnectionFailure("Failed to connect to the host via %s: %s" % (method, to_native(stderr)))
1139        else:
1140            raise AnsibleError("failed to transfer file to %s %s:\n%s\n%s" %
1141                               (to_native(in_path), to_native(out_path), to_native(stdout), to_native(stderr)))
1142
1143    def _escape_win_path(self, path):
1144        """ converts a Windows path to one that's supported by SFTP and SCP """
1145        # If using a root path then we need to start with /
1146        prefix = ""
1147        if re.match(r'^\w{1}:', path):
1148            prefix = "/"
1149
1150        # Convert all '\' to '/'
1151        return "%s%s" % (prefix, path.replace("\\", "/"))
1152
1153    #
1154    # Main public methods
1155    #
1156    def exec_command(self, cmd, in_data=None, sudoable=True):
1157        ''' run a command on the remote host '''
1158
1159        super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
1160
1161        display.vvv(u"ESTABLISH SSH CONNECTION FOR USER: {0}".format(self._play_context.remote_user), host=self._play_context.remote_addr)
1162
1163        if getattr(self._shell, "_IS_WINDOWS", False):
1164            # Become method 'runas' is done in the wrapper that is executed,
1165            # need to disable sudoable so the bare_run is not waiting for a
1166            # prompt that will not occur
1167            sudoable = False
1168
1169            # Make sure our first command is to set the console encoding to
1170            # utf-8, this must be done via chcp to get utf-8 (65001)
1171            cmd_parts = ["chcp.com", "65001", self._shell._SHELL_REDIRECT_ALLNULL, self._shell._SHELL_AND]
1172            cmd_parts.extend(self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False))
1173            cmd = ' '.join(cmd_parts)
1174
1175        # we can only use tty when we are not pipelining the modules. piping
1176        # data into /usr/bin/python inside a tty automatically invokes the
1177        # python interactive-mode but the modules are not compatible with the
1178        # interactive-mode ("unexpected indent" mainly because of empty lines)
1179
1180        ssh_executable = self._play_context.ssh_executable
1181
1182        # -tt can cause various issues in some environments so allow the user
1183        # to disable it as a troubleshooting method.
1184        use_tty = self.get_option('use_tty')
1185
1186        if not in_data and sudoable and use_tty:
1187            args = (ssh_executable, '-tt', self.host, cmd)
1188        else:
1189            args = (ssh_executable, self.host, cmd)
1190
1191        cmd = self._build_command(*args)
1192        (returncode, stdout, stderr) = self._run(cmd, in_data, sudoable=sudoable)
1193
1194        # When running on Windows, stderr may contain CLIXML encoded output
1195        if getattr(self._shell, "_IS_WINDOWS", False) and stderr.startswith(b"#< CLIXML"):
1196            stderr = _parse_clixml(stderr)
1197
1198        return (returncode, stdout, stderr)
1199
1200    def put_file(self, in_path, out_path):
1201        ''' transfer a file from local to remote '''
1202
1203        super(Connection, self).put_file(in_path, out_path)
1204
1205        display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.host)
1206        if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')):
1207            raise AnsibleFileNotFound("file or module does not exist: {0}".format(to_native(in_path)))
1208
1209        if getattr(self._shell, "_IS_WINDOWS", False):
1210            out_path = self._escape_win_path(out_path)
1211
1212        return self._file_transport_command(in_path, out_path, 'put')
1213
1214    def fetch_file(self, in_path, out_path):
1215        ''' fetch a file from remote to local '''
1216
1217        super(Connection, self).fetch_file(in_path, out_path)
1218
1219        display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.host)
1220
1221        # need to add / if path is rooted
1222        if getattr(self._shell, "_IS_WINDOWS", False):
1223            in_path = self._escape_win_path(in_path)
1224
1225        return self._file_transport_command(in_path, out_path, 'get')
1226
1227    def reset(self):
1228        # If we have a persistent ssh connection (ControlPersist), we can ask it to stop listening.
1229        cmd = self._build_command(self._play_context.ssh_executable, '-O', 'stop', self.host)
1230        controlpersist, controlpath = self._persistence_controls(cmd)
1231        cp_arg = [a for a in cmd if a.startswith(b"ControlPath=")]
1232
1233        # only run the reset if the ControlPath already exists or if it isn't
1234        # configured and ControlPersist is set
1235        run_reset = False
1236        if controlpersist and len(cp_arg) > 0:
1237            cp_path = cp_arg[0].split(b"=", 1)[-1]
1238            if os.path.exists(cp_path):
1239                run_reset = True
1240        elif controlpersist:
1241            run_reset = True
1242
1243        if run_reset:
1244            display.vvv(u'sending stop: %s' % to_text(cmd))
1245            p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1246            stdout, stderr = p.communicate()
1247            status_code = p.wait()
1248            if status_code != 0:
1249                display.warning(u"Failed to reset connection:%s" % to_text(stderr))
1250
1251        self.close()
1252
1253    def close(self):
1254        self._connected = False
1255