%PDF- %PDF-
Direktori : /lib/python3/dist-packages/certbot/compat/ |
Current File : //lib/python3/dist-packages/certbot/compat/misc.py |
""" This compat module handles various platform specific calls that do not fall into one particular category. """ from __future__ import absolute_import import logging import select import subprocess import sys import warnings from typing import Optional from typing import Tuple from certbot import errors from certbot.compat import os try: from win32com.shell import shell as shellwin32 from win32console import GetStdHandle, STD_OUTPUT_HANDLE from pywintypes import error as pywinerror POSIX_MODE = False except ImportError: # pragma: no cover POSIX_MODE = True logger = logging.getLogger(__name__) # For Linux: define OS specific standard binary directories STANDARD_BINARY_DIRS = ["/usr/sbin", "/usr/local/bin", "/usr/local/sbin"] if POSIX_MODE else [] def raise_for_non_administrative_windows_rights() -> None: """ On Windows, raise if current shell does not have the administrative rights. Do nothing on Linux. :raises .errors.Error: If the current shell does not have administrative rights on Windows. """ if not POSIX_MODE and shellwin32.IsUserAnAdmin() == 0: # pragma: no cover raise errors.Error('Error, certbot must be run on a shell with administrative rights.') def prepare_virtual_console() -> None: """ On Windows, ensure that Console Virtual Terminal Sequences are enabled. """ if POSIX_MODE: return # https://docs.microsoft.com/en-us/windows/console/setconsolemode ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 # stdout/stderr will be the same console screen buffer, but this could return None or raise try: h = GetStdHandle(STD_OUTPUT_HANDLE) if h: h.SetConsoleMode(h.GetConsoleMode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) except pywinerror: logger.debug("Failed to set console mode", exc_info=True) def readline_with_timeout(timeout: float, prompt: str) -> str: """ Read user input to return the first line entered, or raise after specified timeout. :param float timeout: The timeout in seconds given to the user. :param str prompt: The prompt message to display to the user. :returns: The first line entered by the user. :rtype: str """ try: # Linux specific # # Call to select can only be done like this on UNIX rlist, _, _ = select.select([sys.stdin], [], [], timeout) if not rlist: raise errors.Error( "Timed out waiting for answer to prompt '{0}'".format(prompt)) return rlist[0].readline() except OSError: # Windows specific # # No way with select to make a timeout to the user input on Windows, # as select only supports socket in this case. # So no timeout on Windows for now. return sys.stdin.readline() WINDOWS_DEFAULT_FOLDERS = { 'config': 'C:\\Certbot', 'work': 'C:\\Certbot\\lib', 'logs': 'C:\\Certbot\\log', } LINUX_DEFAULT_FOLDERS = { 'config': '/etc/letsencrypt', 'work': '/var/lib/letsencrypt', 'logs': '/var/log/letsencrypt', } def get_default_folder(folder_type: str) -> str: """ Return the relevant default folder for the current OS :param str folder_type: The type of folder to retrieve (config, work or logs) :returns: The relevant default folder. :rtype: str """ if os.name != 'nt': # Linux specific return LINUX_DEFAULT_FOLDERS[folder_type] # Windows specific return WINDOWS_DEFAULT_FOLDERS[folder_type] def underscores_for_unsupported_characters_in_path(path: str) -> str: """ Replace unsupported characters in path for current OS by underscores. :param str path: the path to normalize :return: the normalized path :rtype: str """ if os.name != 'nt': # Linux specific return path # Windows specific drive, tail = os.path.splitdrive(path) return drive + tail.replace(':', '_') def execute_command_status(cmd_name: str, shell_cmd: str, env: Optional[dict] = None) -> Tuple[int, str, str]: """ Run a command: - on Linux command will be run by the standard shell selected with subprocess.run(shell=True) - on Windows command will be run in a Powershell shell This differs from execute_command: it returns the exit code, and does not log the result and output of the command. :param str cmd_name: the user facing name of the hook being run :param str shell_cmd: shell command to execute :param dict env: environ to pass into subprocess.run :returns: `tuple` (`int` returncode, `str` stderr, `str` stdout) """ logger.info("Running %s command: %s", cmd_name, shell_cmd) if POSIX_MODE: proc = subprocess.run(shell_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, check=False, env=env) else: line = ['powershell.exe', '-Command', shell_cmd] proc = subprocess.run(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, check=False, env=env) # universal_newlines causes stdout and stderr to be str objects instead of # bytes in Python 3 out, err = proc.stdout, proc.stderr return proc.returncode, err, out def execute_command(cmd_name: str, shell_cmd: str, env: Optional[dict] = None) -> Tuple[str, str]: """ Run a command: - on Linux command will be run by the standard shell selected with subprocess.run(shell=True) - on Windows command will be run in a Powershell shell This differs from execute_command: it returns the exit code, and does not log the result and output of the command. :param str cmd_name: the user facing name of the hook being run :param str shell_cmd: shell command to execute :param dict env: environ to pass into subprocess.run :returns: `tuple` (`str` stderr, `str` stdout) """ # Deprecation per https://github.com/certbot/certbot/issues/8854 warnings.warn( "execute_command will be deprecated in the future, use execute_command_status instead", PendingDeprecationWarning ) returncode, err, out = execute_command_status(cmd_name, shell_cmd, env) base_cmd = os.path.basename(shell_cmd.split(None, 1)[0]) if out: logger.info('Output from %s command %s:\n%s', cmd_name, base_cmd, out) if returncode != 0: logger.error('%s command "%s" returned error code %d', cmd_name, shell_cmd, returncode) if err: logger.error('Error output from %s command %s:\n%s', cmd_name, base_cmd, err) return err, out