]> git.ipfire.org Git - pakfire.git/blob - pakfire/util.py
Bump version 0.9.9.
[pakfire.git] / pakfire / util.py
1 #!/usr/bin/python
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 ###############################################################################
21
22 from __future__ import division
23
24 import fcntl
25 import hashlib
26 import logging
27 import math
28 import os
29 import progressbar
30 import random
31 import shutil
32 import signal
33 import string
34 import struct
35 import sys
36 import termios
37 import time
38
39 from constants import *
40 from i18n import _
41
42 # Import binary version of version_compare
43 from _pakfire import version_compare
44
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
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
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
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):
94 # Return nothing if stdout is not a terminal.
95 if not sys.stdout.isatty():
96 return
97
98 widgets = [
99 " ",
100 "%s" % message,
101 " ",
102 Bar(left="[", right="]"),
103 " ",
104 ]
105
106 if eta:
107 widgets += [progressbar.ETA(), " ",]
108
109 if not maxval:
110 maxval = 1
111
112 pb = progressbar.ProgressBar(widgets=widgets, maxval=maxval)
113 pb.start()
114
115 return pb
116
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
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])
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):
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)
239
240 if line:
241 lines.append(" ".join(line))
242
243 assert not words
244
245 #return "\n".join(lines)
246 return lines
247
248 def orphans_kill(root, killsig=signal.SIGTERM):
249 """
250 kill off anything that is still chrooted.
251 """
252 logging.debug("Killing orphans...")
253
254 for fn in [d for d in os.listdir("/proc") if d.isdigit()]:
255 try:
256 r = os.readlink("/proc/%s/root" % fn)
257 if os.path.realpath(root) == os.path.realpath(r):
258 logging.warning("Process ID %s is still running in chroot. Killing..." % fn)
259
260 pid = int(fn, 10)
261 os.kill(pid, killsig)
262 os.waitpid(pid, 0)
263 except OSError, e:
264 pass
265
266 def scriptlet_interpreter(scriptlet):
267 """
268 This function returns the interpreter of a scriptlet.
269 """
270 # XXX handle ELF?
271 interpreter = None
272
273 for line in scriptlet.splitlines():
274 if line.startswith("#!/"):
275 interpreter = line[2:]
276 interpreter = interpreter.split()[0]
277 break
278
279 return interpreter
280
281 def calc_parallelism():
282 """
283 Calculate how many processes to run
284 at the same time.
285
286 We take the log10(number of processors) * factor
287 """
288 num = os.sysconf("SC_NPROCESSORS_CONF")
289 if num == 1:
290 return 2
291 else:
292 return int(round(math.log10(num) * 26))