]>
Commit | Line | Data |
---|---|---|
47a4cb89 | 1 | #!/usr/bin/python |
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 MT |
22 | from __future__ import division |
23 | ||
0a9a2371 | 24 | import fcntl |
102c5ee7 | 25 | import hashlib |
aa7f15fe | 26 | import logging |
c07a3ca7 | 27 | import math |
47a4cb89 | 28 | import os |
4496b160 | 29 | import progressbar |
47a4cb89 | 30 | import random |
47a4cb89 | 31 | import shutil |
aa7f15fe | 32 | import signal |
47a4cb89 | 33 | import string |
0a9a2371 | 34 | import struct |
47a4cb89 | 35 | import sys |
0a9a2371 | 36 | import termios |
47a4cb89 MT |
37 | import time |
38 | ||
102c5ee7 | 39 | from constants import * |
c0fd807c | 40 | from i18n import _ |
47a4cb89 | 41 | |
102c5ee7 MT |
42 | # Import binary version of version_compare |
43 | from _pakfire import version_compare | |
44 | ||
e9c20259 MT |
45 | def cli_is_interactive(): |
46 | """ | |
47 | Say weather a shell is interactive or not. | |
48 | """ | |
49 | if sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty(): | |
50 | return True | |
51 | ||
52 | return False | |
53 | ||
c0fd807c MT |
54 | def ask_user(question): |
55 | """ | |
56 | Ask the user the question, he or she can answer with yes or no. | |
57 | ||
58 | This function returns True for "yes" and False for "no". | |
59 | ||
60 | If the software is running in a non-inteactive shell, no question | |
61 | is asked at all and the answer is always "yes". | |
62 | """ | |
63 | if not cli_is_interactive(): | |
64 | return True | |
65 | ||
66 | print _("%s [y/N]") % question, | |
67 | ret = raw_input() | |
68 | print # Just an empty line. | |
69 | ||
70 | return ret in ("y", "Y", "z", "Z", "j", "J") | |
71 | ||
7c8f2953 MT |
72 | def random_string(length=20): |
73 | s = "" | |
74 | ||
75 | for i in range(length): | |
76 | s += random.choice(string.letters) | |
77 | ||
78 | return s | |
79 | ||
1e80d5d7 MT |
80 | |
81 | class Bar(progressbar.Bar): | |
82 | def update(self, pbar, width): | |
83 | percent = pbar.percentage() | |
84 | if pbar.finished: | |
85 | return " " * width | |
86 | ||
87 | cwidth = width - len(self.left) - len(self.right) | |
88 | marked_width = int(percent * cwidth / 100) | |
89 | m = self._format_marker(pbar) | |
90 | bar = (self.left + (m*marked_width).ljust(cwidth) + self.right) | |
91 | return bar | |
92 | ||
93 | def make_progress(message, maxval, eta=True): | |
4496b160 MT |
94 | # Return nothing if stdout is not a terminal. |
95 | if not sys.stdout.isatty(): | |
96 | return | |
97 | ||
98 | widgets = [ | |
99 | " ", | |
e0636b31 | 100 | "%s" % message, |
4496b160 | 101 | " ", |
1e80d5d7 | 102 | Bar(left="[", right="]"), |
4496b160 MT |
103 | " ", |
104 | ] | |
105 | ||
1e80d5d7 MT |
106 | if eta: |
107 | widgets += [progressbar.ETA(), " ",] | |
108 | ||
4496b160 MT |
109 | if not maxval: |
110 | maxval = 1 | |
111 | ||
112 | pb = progressbar.ProgressBar(widgets=widgets, maxval=maxval) | |
113 | pb.start() | |
114 | ||
115 | return pb | |
116 | ||
47a4cb89 MT |
117 | def rm(path, *args, **kargs): |
118 | """ | |
119 | version of shutil.rmtree that ignores no-such-file-or-directory errors, | |
120 | and tries harder if it finds immutable files | |
121 | """ | |
122 | tryAgain = 1 | |
123 | failedFilename = None | |
124 | while tryAgain: | |
125 | tryAgain = 0 | |
126 | try: | |
127 | shutil.rmtree(path, *args, **kargs) | |
128 | except OSError, e: | |
129 | if e.errno == 2: # no such file or directory | |
130 | pass | |
131 | elif e.errno==1 or e.errno==13: | |
132 | tryAgain = 1 | |
133 | if failedFilename == e.filename: | |
134 | raise | |
135 | failedFilename = e.filename | |
136 | os.system("chattr -R -i %s" % path) | |
137 | else: | |
138 | raise | |
0a9a2371 MT |
139 | |
140 | def ioctl_GWINSZ(fd): | |
141 | try: | |
142 | cr = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) | |
143 | except: | |
144 | return None | |
145 | ||
146 | return cr | |
147 | ||
148 | def terminal_size(): | |
149 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) | |
150 | ||
151 | if not cr: | |
152 | try: | |
153 | fd = os.open(os.ctermid(), os.O_RDONLY) | |
154 | cr = ioctl_GWINSZ(fd) | |
155 | os.close(fd) | |
156 | except: | |
157 | pass | |
158 | ||
159 | if not cr: | |
160 | try: | |
161 | cr = (os.environ['LINES'], os.environ['COLUMNS']) | |
162 | except: | |
163 | cr = (25, 80) | |
164 | ||
165 | return int(cr[1]), int(cr[0]) | |
102c5ee7 MT |
166 | |
167 | def format_size(s): | |
168 | sign = 1 | |
169 | ||
170 | # If s is negative, we save the sign and run the calculation with the | |
171 | # absolute value of s. | |
172 | if s < 0: | |
173 | sign = -1 | |
174 | s = -1 * s | |
175 | ||
176 | units = (" ", "k", "M", "G", "T") | |
177 | unit = 0 | |
178 | ||
179 | while s >= 1024 and unit < len(units): | |
180 | s /= 1024 | |
181 | unit += 1 | |
182 | ||
183 | return "%d %s" % (int(s) * sign, units[unit]) | |
184 | ||
185 | def format_time(s): | |
186 | return "%02d:%02d" % (s // 60, s % 60) | |
187 | ||
188 | def format_speed(s): | |
189 | return "%sB/s" % format_size(s) | |
190 | ||
191 | def calc_hash1(filename=None, data=None): | |
192 | h = hashlib.sha1() | |
193 | ||
194 | if filename: | |
195 | f = open(filename) | |
196 | buf = f.read(BUFFER_SIZE) | |
197 | while buf: | |
198 | h.update(buf) | |
199 | buf = f.read(BUFFER_SIZE) | |
200 | ||
201 | f.close() | |
202 | ||
203 | elif data: | |
204 | h.update(data) | |
205 | ||
206 | return h.hexdigest() | |
207 | ||
208 | def text_wrap(s, length=65): | |
c07a3ca7 MT |
209 | if not s: |
210 | return "" | |
211 | ||
212 | lines = [] | |
213 | ||
214 | words = [] | |
215 | for line in s.splitlines(): | |
216 | if not line: | |
217 | words.append("") | |
218 | else: | |
219 | words += line.split() | |
220 | ||
221 | line = [] | |
222 | while words: | |
223 | word = words.pop(0) | |
224 | ||
225 | # An empty words means a line break. | |
226 | if not word: | |
227 | if line: | |
228 | lines.append(" ".join(line)) | |
229 | lines.append("") | |
230 | line = [] | |
231 | ||
232 | else: | |
233 | if len(" ".join(line)) + len(word) >= length: | |
234 | lines.append(" ".join(line)) | |
235 | line = [] | |
236 | words.insert(0, word) | |
237 | else: | |
238 | line.append(word) | |
102c5ee7 | 239 | |
c07a3ca7 MT |
240 | if line: |
241 | lines.append(" ".join(line)) | |
102c5ee7 | 242 | |
c07a3ca7 | 243 | assert not words |
102c5ee7 | 244 | |
c07a3ca7 MT |
245 | #return "\n".join(lines) |
246 | return lines | |
aa7f15fe MT |
247 | |
248 | def orphans_kill(root, killsig=signal.SIGTERM): | |
249 | """ | |
250 | kill off anything that is still chrooted. | |
251 | """ | |
536438d9 | 252 | logging.debug(_("Killing orphans...")) |
aa7f15fe | 253 | |
536438d9 | 254 | killed = False |
aa7f15fe MT |
255 | for fn in [d for d in os.listdir("/proc") if d.isdigit()]: |
256 | try: | |
257 | r = os.readlink("/proc/%s/root" % fn) | |
258 | if os.path.realpath(root) == os.path.realpath(r): | |
536438d9 MT |
259 | logging.warning(_("Process ID %s is still running in chroot. Killing...") % fn) |
260 | killed = True | |
aa7f15fe MT |
261 | |
262 | pid = int(fn, 10) | |
263 | os.kill(pid, killsig) | |
264 | os.waitpid(pid, 0) | |
265 | except OSError, e: | |
266 | pass | |
c07a3ca7 | 267 | |
536438d9 MT |
268 | # If something was killed, wait a couple of seconds to make sure all file descriptors |
269 | # are closed and we can proceed with umounting the filesystems. | |
270 | if killed: | |
271 | logging.warning(_("Waiting for processes to terminate...")) | |
272 | time.sleep(3) | |
273 | ||
c07a3ca7 MT |
274 | def scriptlet_interpreter(scriptlet): |
275 | """ | |
276 | This function returns the interpreter of a scriptlet. | |
277 | """ | |
278 | # XXX handle ELF? | |
279 | interpreter = None | |
280 | ||
281 | for line in scriptlet.splitlines(): | |
282 | if line.startswith("#!/"): | |
283 | interpreter = line[2:] | |
284 | interpreter = interpreter.split()[0] | |
285 | break | |
286 | ||
287 | return interpreter | |
288 | ||
289 | def calc_parallelism(): | |
290 | """ | |
291 | Calculate how many processes to run | |
292 | at the same time. | |
293 | ||
294 | We take the log10(number of processors) * factor | |
295 | """ | |
296 | num = os.sysconf("SC_NPROCESSORS_CONF") | |
297 | if num == 1: | |
298 | return 2 | |
299 | else: | |
300 | return int(round(math.log10(num) * 26)) |