1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
"""
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
from certbot import errors
from certbot.compat import os
from acme.magic_typing import Tuple
try:
from win32com.shell import shell as shellwin32
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():
# type: () -> 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 readline_with_timeout(timeout, prompt):
# type: (float, 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):
# 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):
# type: (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(cmd_name, shell_cmd):
# type: (str, str) -> Tuple[str, str]
"""
Run a command:
- on Linux command will be run by the standard shell selected with Popen(shell=True)
- on Windows command will be run in a Powershell shell
:param str cmd_name: the user facing name of the hook being run
:param str shell_cmd: shell command to execute
:returns: `tuple` (`str` stderr, `str` stdout)
"""
logger.info("Running %s command: %s", cmd_name, shell_cmd)
if POSIX_MODE:
cmd = subprocess.Popen(shell_cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True)
else:
line = ['powershell.exe', '-Command', shell_cmd]
cmd = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
# universal_newlines causes Popen.communicate()
# to return str objects instead of bytes in Python 3
out, err = cmd.communicate()
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 cmd.returncode != 0:
logger.error('%s command "%s" returned error code %d',
cmd_name, shell_cmd, cmd.returncode)
if err:
logger.error('Error output from %s command %s:\n%s', cmd_name, base_cmd, err)
return err, out
|