def sudo(command, shell=True, user=None, pty=False): """ Run a shell command on a remote host, with superuser privileges. As with ``run()``, ``sudo()`` executes within a shell command defaulting to the value of ``env.shell``, although it goes one step further and wraps the command with ``sudo`` as well. Also similar to ``run()``, the shell You may specify a ``user`` keyword argument, which is passed to ``sudo`` and allows you to run as some user other than root (which is the default). On most systems, the ``sudo`` program can take a string username or an integer userid (uid); ``user`` may likewise be a string or an int. Some remote systems may be configured to disallow sudo access unless a terminal or pseudoterminal is being used (e.g. when ``Defaults requiretty`` exists in ``/etc/sudoers``.) If updating the remote system's ``sudoers`` configuration is not possible or desired, you may pass ``pty=True`` to `sudo` to force allocation of a pseudo tty on the remote end. `sudo` will return the result of the remote program's stdout as a single (likely multiline) string. This string will exhibit a ``failed`` boolean attribute specifying whether the command failed or succeeded, and will also include the return code as the ``return_code`` attribute. Examples:: sudo("~/install_script.py") sudo("mkdir /var/www/new_docroot", user="******") sudo("ls /home/jdoe", user=1001) result = sudo("ls /tmp/") """ # Construct sudo command, with user if necessary if user is not None: if str(user).isdigit(): user = "******" % user sudo_prefix = "sudo -S -p '%%s' -u \"%s\" " % user else: sudo_prefix = "sudo -S -p '%s' " # Put in explicit sudo prompt string (so we know what to look for when # detecting prompts) sudo_prefix = sudo_prefix % env.sudo_prompt # Without using a shell, we just do 'sudo -u blah my_command' if (not env.use_shell) or (not shell): real_command = "%s %s" % (sudo_prefix, _shell_escape(command)) # With a shell, we do 'sudo -u blah /bin/bash -l -c "my_command"' else: # With a shell, we can also honor cwd cwd = env.get('cwd', '') if cwd: # TODO: see if there is any nice way to quote this, given that it # ends up inside double quotes down below... cwd = 'cd %s && ' % _shell_escape(cwd) real_command = '%s %s "%s"' % (sudo_prefix, env.shell, _shell_escape(cwd + command)) # TODO: handle confirm_proceed behavior, as in run() if output.debug: print("[%s] sudo: %s" % (env.host_string, real_command)) elif output.running: print("[%s] sudo: %s" % (env.host_string, command)) channel = connections[env.host_string]._transport.open_session() # Create pty if necessary (using Paramiko default options, which as of # 1.7.4 is vt100 $TERM @ 80x24 characters) if pty: channel.get_pty() # Execute channel.exec_command(real_command) capture = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True) # Close channel when done status = channel.recv_exit_status() # Wait for threads to exit before returning (otherwise we will occasionally # end up returning before the threads have fully wrapped up) out_thread.join() err_thread.join() # Close channel channel.close() # Assemble stdout string out = _AttributeString("".join(capture).strip()) # Error handling out.failed = False if status != 0: out.failed = True msg = "sudo() encountered an error (return code %s) while executing '%s'" % (status, command) _handle_failure(message=msg) # Attach return code for convenience out.return_code = status return out
def sudo(command, shell=True, user=None): """ Run a shell command on a remote host, with superuser privileges. As with ``run()``, ``sudo()`` executes within a shell command defaulting to the value of ``env.shell``, although it goes one step further and wraps the command with ``sudo`` as well. Also similar to ``run()``, the shell You may specify a ``user`` keyword argument, which is passed to ``sudo`` and allows you to run as some user other than root (which is the default). On most systems, the ``sudo`` program can take a string username or an integer userid (uid); ``user`` may likewise be a string or an int. `sudo` will return the result of the remote program's stdout as a single (likely multiline) string. This string will exhibit a ``failed`` boolean attribute specifying whether the command failed or succeeded, and will also include the return code as the ``return_code`` attribute. Examples:: sudo("~/install_script.py") sudo("mkdir /var/www/new_docroot", user="******") sudo("ls /home/jdoe", user=1001) result = sudo("ls /tmp/") """ # Construct sudo command, with user if necessary if user is not None: if str(user).isint(): user = "******" % user sudo_prefix = "sudo -S -p '%%s' -u \"%s\" " % user else: sudo_prefix = "sudo -S -p '%s' " # Put in explicit sudo prompt string (so we know what to look for when # detecting prompts) sudo_prefix = sudo_prefix % env.sudo_prompt # Without using a shell, we just do 'sudo -u blah my_command' if (not env.use_shell) or (not shell): real_command = "%s %s" % (sudo_prefix, _shell_escape(command)) # With a shell, we do 'sudo -u blah /bin/bash -l -c "my_command"' else: real_command = '%s %s "%s"' % (sudo_prefix, env.shell, _shell_escape(command)) # TODO: tie this into global output controls; both in terms of showing the # shell itself, AND showing the sudo prefix. Not 100% sure it's worth being # so granular as to allow one on and one off, but think about it. # TODO: handle confirm_proceed behavior, as in run() if env.debug: print("[%s] sudo: %s" % (env.host_string, real_command)) else: print("[%s] sudo: %s" % (env.host_string, command)) channel = connections[env.host_string]._transport.open_session() channel.exec_command(real_command) capture = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True) # Close channel when done status = channel.recv_exit_status() channel.close() # Wait for threads to exit before returning (otherwise we will occasionally # end up returning before the threads have fully wrapped up) out_thread.join() err_thread.join() # Assemble stdout string output = _AttributeString("".join(capture).strip()) # Error handling output.failed = False if status != 0: output.failed = True msg = "sudo() encountered an error (return code %s) while executing '%s'" % (status, command) _handle_failure(message=msg) # Attach return code for convenience output.return_code = status return output
def run(command, shell=True, pty=False): """ Run a shell command on a remote host. If ``shell`` is True (the default), ``run()`` will execute the given command string via a shell interpreter, the value of which may be controlled by setting ``env.shell`` (defaulting to something similar to ``/bin/bash -l -c "<command>"``.) Any double-quote (``"``) characters in ``command`` will be automatically escaped when ``shell`` is True. `run` will return the result of the remote program's stdout as a single (likely multiline) string. This string will exhibit a ``failed`` boolean attribute specifying whether the command failed or succeeded, and will also include the return code as the ``return_code`` attribute. You may pass ``pty=True`` to force allocation of a pseudo tty on the remote end. This is not normally required, but some programs may complain (or, even more rarely, refuse to run) if a tty is not present. Examples:: run("ls /var/www/") run("ls /home/myuser", shell=False) output = run('ls /var/www/site1') """ # Set up new var so original argument can be displayed verbatim later. real_command = command if shell: # Handle cwd munging via 'cd' context manager cwd = env.get('cwd', '') if cwd: # TODO: see if there is any nice way to quote this, given that it # ends up inside double quotes down below... cwd = 'cd %s && ' % _shell_escape(cwd) # Construct final real, full command real_command = '%s "%s"' % (env.shell, _shell_escape(cwd + real_command)) # TODO: possibly put back in previously undocumented 'confirm_proceed' # functionality, i.e. users may set an option to be prompted before each # execution. Pretty sure this should be a global option applying to ALL # remote operations! And, of course -- documented. if output.debug: print("[%s] run: %s" % (env.host_string, real_command)) elif output.running: print("[%s] run: %s" % (env.host_string, command)) channel = connections[env.host_string]._transport.open_session() # Create pty if necessary (using Paramiko default options, which as of # 1.7.4 is vt100 $TERM @ 80x24 characters) if pty: channel.get_pty() channel.exec_command(real_command) capture = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True) # Close when done status = channel.recv_exit_status() # Wait for threads to exit so we aren't left with stale threads out_thread.join() err_thread.join() # Close channel channel.close() # Assemble output string out = _AttributeString("".join(capture).strip()) # Error handling out.failed = False if status != 0: out.failed = True msg = "run() encountered an error (return code %s) while executing '%s'" % (status, command) _handle_failure(message=msg) # Attach return code to output string so users who have set things to warn # only, can inspect the error code. out.return_code = status return out
def run(command, shell=True): """ Run a shell command on a remote host. If ``shell`` is True (the default), ``run()`` will execute the given command string via a shell interpreter, the value of which may be controlled by setting ``env.shell`` (defaulting to something similar to ``/bin/bash -l -c "<command>"``.) Any double-quote (``"``) characters in ``command`` will be automatically escaped when ``shell`` is True. `run` will return the result of the remote program's stdout as a single (likely multiline) string. This string will exhibit a ``failed`` boolean attribute specifying whether the command failed or succeeded, and will also include the return code as the ``return_code`` attribute. Examples:: run("ls /var/www/") run("ls /home/myuser", shell=False) output = run('ls /var/www/site1') """ real_command = command if shell: real_command = '%s "%s"' % (env.shell, _shell_escape(command)) # TODO: possibly put back in previously undocumented 'confirm_proceed' # functionality, i.e. users may set an option to be prompted before each # execution. Pretty sure this should be a global option applying to ALL # remote operations! And, of course -- documented. # TODO: tie this into global output controls if env.debug: print("[%s] run: %s" % (env.host_string, real_command)) else: print("[%s] run: %s" % (env.host_string, command)) channel = connections[env.host_string]._transport.open_session() channel.exec_command(real_command) capture = [] # TODO: tie into global output controls out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True) # Close when done status = channel.recv_exit_status() channel.close() # Wait for threads to exit so we aren't left with stale threads out_thread.join() err_thread.join() # Assemble output string output = _AttributeString("".join(capture).strip()) # Error handling output.failed = False if status != 0: output.failed = True msg = "run() encountered an error (return code %s) while executing '%s'" % (status, command) _handle_failure(message=msg) # Attach return code to output string so users who have set things to warn # only, can inspect the error code. output.return_code = status return output