]>
Commit | Line | Data |
---|---|---|
964aa579 | 1 | #!/usr/bin/python3 |
b792d887 MT |
2 | ############################################################################### |
3 | # # | |
4 | # Pakfire - The IPFire package management system # | |
5 | # Copyright (C) 2011 Pakfire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
47a4cb89 | 21 | |
102c5ee7 | 22 | import hashlib |
47a4cb89 MT |
23 | import os |
24 | import random | |
47a4cb89 | 25 | import shutil |
aa7f15fe | 26 | import signal |
47a4cb89 | 27 | import string |
47a4cb89 MT |
28 | import sys |
29 | import time | |
30 | ||
8b6bc023 MT |
31 | import logging |
32 | log = logging.getLogger("pakfire") | |
33 | ||
964aa579 MT |
34 | from .constants import * |
35 | from .i18n import _ | |
47a4cb89 | 36 | |
3c0e282f | 37 | # Import binary version of version_compare and capability functions |
44b27b6f | 38 | from ._pakfire import get_capabilities, set_capabilities, personality |
102c5ee7 | 39 | |
e9c20259 MT |
40 | def cli_is_interactive(): |
41 | """ | |
42 | Say weather a shell is interactive or not. | |
43 | """ | |
44 | if sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty(): | |
45 | return True | |
46 | ||
47 | return False | |
48 | ||
1b61ba46 MT |
49 | def is_enabled(s): |
50 | return s in ("true", "yes", "1") | |
51 | ||
c0fd807c MT |
52 | def ask_user(question): |
53 | """ | |
54 | Ask the user the question, he or she can answer with yes or no. | |
55 | ||
56 | This function returns True for "yes" and False for "no". | |
57 | ||
58 | If the software is running in a non-inteactive shell, no question | |
59 | is asked at all and the answer is always "yes". | |
60 | """ | |
61 | if not cli_is_interactive(): | |
62 | return True | |
63 | ||
964aa579 MT |
64 | print(_("%s [y/N]") % question, end=' ') |
65 | ret = input() | |
66 | print() # Just an empty line. | |
c0fd807c MT |
67 | |
68 | return ret in ("y", "Y", "z", "Z", "j", "J") | |
69 | ||
7c8f2953 MT |
70 | def random_string(length=20): |
71 | s = "" | |
72 | ||
73 | for i in range(length): | |
964aa579 | 74 | s += random.choice(string.ascii_letters) |
7c8f2953 MT |
75 | |
76 | return s | |
77 | ||
38ba21f9 | 78 | def make_progress(message, maxval, eta=True, speed=False): |
964aa579 MT |
79 | # XXX delay importing the progressbar module |
80 | # (because of a circular dependency) | |
10458aa0 | 81 | from .ui import progressbar |
964aa579 | 82 | |
4496b160 MT |
83 | # Return nothing if stdout is not a terminal. |
84 | if not sys.stdout.isatty(): | |
85 | return | |
86 | ||
dccd65ea MT |
87 | if not maxval: |
88 | maxval = 1 | |
4496b160 | 89 | |
dccd65ea MT |
90 | pb = progressbar.ProgressBar(maxval) |
91 | pb.add("%-50s" % message) | |
38ba21f9 | 92 | |
dccd65ea MT |
93 | bar = progressbar.WidgetBar() |
94 | pb.add(bar) | |
1e80d5d7 | 95 | |
dccd65ea MT |
96 | if speed: |
97 | percentage = progressbar.WidgetPercentage() | |
98 | pb.add(percentage) | |
4496b160 | 99 | |
dccd65ea MT |
100 | filetransfer = progressbar.WidgetFileTransferSpeed() |
101 | pb.add(filetransfer) | |
4496b160 | 102 | |
dccd65ea MT |
103 | if eta: |
104 | eta = progressbar.WidgetETA() | |
105 | pb.add(eta) | |
106 | ||
107 | return pb.start() | |
4496b160 | 108 | |
47a4cb89 MT |
109 | def rm(path, *args, **kargs): |
110 | """ | |
111 | version of shutil.rmtree that ignores no-such-file-or-directory errors, | |
112 | and tries harder if it finds immutable files | |
113 | """ | |
114 | tryAgain = 1 | |
115 | failedFilename = None | |
116 | while tryAgain: | |
117 | tryAgain = 0 | |
118 | try: | |
119 | shutil.rmtree(path, *args, **kargs) | |
964aa579 | 120 | except OSError as e: |
47a4cb89 MT |
121 | if e.errno == 2: # no such file or directory |
122 | pass | |
123 | elif e.errno==1 or e.errno==13: | |
124 | tryAgain = 1 | |
125 | if failedFilename == e.filename: | |
126 | raise | |
127 | failedFilename = e.filename | |
128 | os.system("chattr -R -i %s" % path) | |
129 | else: | |
130 | raise | |
0a9a2371 | 131 | |
102c5ee7 | 132 | def calc_hash1(filename=None, data=None): |
e3bcfd23 | 133 | h = hashlib.new("sha1") |
102c5ee7 MT |
134 | |
135 | if filename: | |
136 | f = open(filename) | |
137 | buf = f.read(BUFFER_SIZE) | |
138 | while buf: | |
139 | h.update(buf) | |
140 | buf = f.read(BUFFER_SIZE) | |
141 | ||
142 | f.close() | |
143 | ||
144 | elif data: | |
145 | h.update(data) | |
146 | ||
147 | return h.hexdigest() | |
148 | ||
149 | def text_wrap(s, length=65): | |
c07a3ca7 MT |
150 | if not s: |
151 | return "" | |
152 | ||
153 | lines = [] | |
154 | ||
155 | words = [] | |
156 | for line in s.splitlines(): | |
157 | if not line: | |
158 | words.append("") | |
159 | else: | |
160 | words += line.split() | |
161 | ||
162 | line = [] | |
163 | while words: | |
164 | word = words.pop(0) | |
165 | ||
166 | # An empty words means a line break. | |
167 | if not word: | |
168 | if line: | |
169 | lines.append(" ".join(line)) | |
170 | lines.append("") | |
171 | line = [] | |
172 | ||
173 | else: | |
174 | if len(" ".join(line)) + len(word) >= length: | |
175 | lines.append(" ".join(line)) | |
176 | line = [] | |
177 | words.insert(0, word) | |
178 | else: | |
179 | line.append(word) | |
102c5ee7 | 180 | |
c07a3ca7 MT |
181 | if line: |
182 | lines.append(" ".join(line)) | |
102c5ee7 | 183 | |
c07a3ca7 | 184 | assert not words |
102c5ee7 | 185 | |
c07a3ca7 MT |
186 | #return "\n".join(lines) |
187 | return lines | |
aa7f15fe MT |
188 | |
189 | def orphans_kill(root, killsig=signal.SIGTERM): | |
190 | """ | |
191 | kill off anything that is still chrooted. | |
192 | """ | |
8b6bc023 | 193 | log.debug(_("Killing orphans...")) |
aa7f15fe | 194 | |
536438d9 | 195 | killed = False |
aa7f15fe MT |
196 | for fn in [d for d in os.listdir("/proc") if d.isdigit()]: |
197 | try: | |
198 | r = os.readlink("/proc/%s/root" % fn) | |
199 | if os.path.realpath(root) == os.path.realpath(r): | |
8b6bc023 | 200 | log.warning(_("Process ID %s is still running in chroot. Killing...") % fn) |
536438d9 | 201 | killed = True |
aa7f15fe MT |
202 | |
203 | pid = int(fn, 10) | |
204 | os.kill(pid, killsig) | |
205 | os.waitpid(pid, 0) | |
964aa579 | 206 | except OSError as e: |
aa7f15fe | 207 | pass |
c07a3ca7 | 208 | |
536438d9 MT |
209 | # If something was killed, wait a couple of seconds to make sure all file descriptors |
210 | # are closed and we can proceed with umounting the filesystems. | |
211 | if killed: | |
8b6bc023 | 212 | log.warning(_("Waiting for processes to terminate...")) |
536438d9 MT |
213 | time.sleep(3) |
214 | ||
7814c06d MT |
215 | # Calling ourself again to make sure all processes were killed. |
216 | orphans_kill(root, killsig=killsig) | |
217 | ||
c07a3ca7 MT |
218 | def scriptlet_interpreter(scriptlet): |
219 | """ | |
220 | This function returns the interpreter of a scriptlet. | |
221 | """ | |
222 | # XXX handle ELF? | |
223 | interpreter = None | |
224 | ||
225 | for line in scriptlet.splitlines(): | |
226 | if line.startswith("#!/"): | |
227 | interpreter = line[2:] | |
228 | interpreter = interpreter.split()[0] | |
229 | break | |
230 | ||
231 | return interpreter |