]>
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 |
964aa579 | 38 | from ._pakfire import version_compare, 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 | ||
c0fd807c MT |
49 | def ask_user(question): |
50 | """ | |
51 | Ask the user the question, he or she can answer with yes or no. | |
52 | ||
53 | This function returns True for "yes" and False for "no". | |
54 | ||
55 | If the software is running in a non-inteactive shell, no question | |
56 | is asked at all and the answer is always "yes". | |
57 | """ | |
58 | if not cli_is_interactive(): | |
59 | return True | |
60 | ||
964aa579 MT |
61 | print(_("%s [y/N]") % question, end=' ') |
62 | ret = input() | |
63 | print() # Just an empty line. | |
c0fd807c MT |
64 | |
65 | return ret in ("y", "Y", "z", "Z", "j", "J") | |
66 | ||
7c8f2953 MT |
67 | def random_string(length=20): |
68 | s = "" | |
69 | ||
70 | for i in range(length): | |
964aa579 | 71 | s += random.choice(string.ascii_letters) |
7c8f2953 MT |
72 | |
73 | return s | |
74 | ||
38ba21f9 | 75 | def make_progress(message, maxval, eta=True, speed=False): |
964aa579 MT |
76 | # XXX delay importing the progressbar module |
77 | # (because of a circular dependency) | |
10458aa0 | 78 | from .ui import progressbar |
964aa579 | 79 | |
4496b160 MT |
80 | # Return nothing if stdout is not a terminal. |
81 | if not sys.stdout.isatty(): | |
82 | return | |
83 | ||
dccd65ea MT |
84 | if not maxval: |
85 | maxval = 1 | |
4496b160 | 86 | |
dccd65ea MT |
87 | pb = progressbar.ProgressBar(maxval) |
88 | pb.add("%-50s" % message) | |
38ba21f9 | 89 | |
dccd65ea MT |
90 | bar = progressbar.WidgetBar() |
91 | pb.add(bar) | |
1e80d5d7 | 92 | |
dccd65ea MT |
93 | if speed: |
94 | percentage = progressbar.WidgetPercentage() | |
95 | pb.add(percentage) | |
4496b160 | 96 | |
dccd65ea MT |
97 | filetransfer = progressbar.WidgetFileTransferSpeed() |
98 | pb.add(filetransfer) | |
4496b160 | 99 | |
dccd65ea MT |
100 | if eta: |
101 | eta = progressbar.WidgetETA() | |
102 | pb.add(eta) | |
103 | ||
104 | return pb.start() | |
4496b160 | 105 | |
47a4cb89 MT |
106 | def rm(path, *args, **kargs): |
107 | """ | |
108 | version of shutil.rmtree that ignores no-such-file-or-directory errors, | |
109 | and tries harder if it finds immutable files | |
110 | """ | |
111 | tryAgain = 1 | |
112 | failedFilename = None | |
113 | while tryAgain: | |
114 | tryAgain = 0 | |
115 | try: | |
116 | shutil.rmtree(path, *args, **kargs) | |
964aa579 | 117 | except OSError as e: |
47a4cb89 MT |
118 | if e.errno == 2: # no such file or directory |
119 | pass | |
120 | elif e.errno==1 or e.errno==13: | |
121 | tryAgain = 1 | |
122 | if failedFilename == e.filename: | |
123 | raise | |
124 | failedFilename = e.filename | |
125 | os.system("chattr -R -i %s" % path) | |
126 | else: | |
127 | raise | |
0a9a2371 | 128 | |
102c5ee7 | 129 | def calc_hash1(filename=None, data=None): |
e3bcfd23 | 130 | h = hashlib.new("sha1") |
102c5ee7 MT |
131 | |
132 | if filename: | |
133 | f = open(filename) | |
134 | buf = f.read(BUFFER_SIZE) | |
135 | while buf: | |
136 | h.update(buf) | |
137 | buf = f.read(BUFFER_SIZE) | |
138 | ||
139 | f.close() | |
140 | ||
141 | elif data: | |
142 | h.update(data) | |
143 | ||
144 | return h.hexdigest() | |
145 | ||
146 | def text_wrap(s, length=65): | |
c07a3ca7 MT |
147 | if not s: |
148 | return "" | |
149 | ||
150 | lines = [] | |
151 | ||
152 | words = [] | |
153 | for line in s.splitlines(): | |
154 | if not line: | |
155 | words.append("") | |
156 | else: | |
157 | words += line.split() | |
158 | ||
159 | line = [] | |
160 | while words: | |
161 | word = words.pop(0) | |
162 | ||
163 | # An empty words means a line break. | |
164 | if not word: | |
165 | if line: | |
166 | lines.append(" ".join(line)) | |
167 | lines.append("") | |
168 | line = [] | |
169 | ||
170 | else: | |
171 | if len(" ".join(line)) + len(word) >= length: | |
172 | lines.append(" ".join(line)) | |
173 | line = [] | |
174 | words.insert(0, word) | |
175 | else: | |
176 | line.append(word) | |
102c5ee7 | 177 | |
c07a3ca7 MT |
178 | if line: |
179 | lines.append(" ".join(line)) | |
102c5ee7 | 180 | |
c07a3ca7 | 181 | assert not words |
102c5ee7 | 182 | |
c07a3ca7 MT |
183 | #return "\n".join(lines) |
184 | return lines | |
aa7f15fe MT |
185 | |
186 | def orphans_kill(root, killsig=signal.SIGTERM): | |
187 | """ | |
188 | kill off anything that is still chrooted. | |
189 | """ | |
8b6bc023 | 190 | log.debug(_("Killing orphans...")) |
aa7f15fe | 191 | |
536438d9 | 192 | killed = False |
aa7f15fe MT |
193 | for fn in [d for d in os.listdir("/proc") if d.isdigit()]: |
194 | try: | |
195 | r = os.readlink("/proc/%s/root" % fn) | |
196 | if os.path.realpath(root) == os.path.realpath(r): | |
8b6bc023 | 197 | log.warning(_("Process ID %s is still running in chroot. Killing...") % fn) |
536438d9 | 198 | killed = True |
aa7f15fe MT |
199 | |
200 | pid = int(fn, 10) | |
201 | os.kill(pid, killsig) | |
202 | os.waitpid(pid, 0) | |
964aa579 | 203 | except OSError as e: |
aa7f15fe | 204 | pass |
c07a3ca7 | 205 | |
536438d9 MT |
206 | # If something was killed, wait a couple of seconds to make sure all file descriptors |
207 | # are closed and we can proceed with umounting the filesystems. | |
208 | if killed: | |
8b6bc023 | 209 | log.warning(_("Waiting for processes to terminate...")) |
536438d9 MT |
210 | time.sleep(3) |
211 | ||
7814c06d MT |
212 | # Calling ourself again to make sure all processes were killed. |
213 | orphans_kill(root, killsig=killsig) | |
214 | ||
c07a3ca7 MT |
215 | def scriptlet_interpreter(scriptlet): |
216 | """ | |
217 | This function returns the interpreter of a scriptlet. | |
218 | """ | |
219 | # XXX handle ELF? | |
220 | interpreter = None | |
221 | ||
222 | for line in scriptlet.splitlines(): | |
223 | if line.startswith("#!/"): | |
224 | interpreter = line[2:] | |
225 | interpreter = interpreter.split()[0] | |
226 | break | |
227 | ||
228 | return interpreter |