]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/util.py
Better personlity function.
[pakfire.git] / python / 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 and capability functions
43 from _pakfire import version_compare, get_capabilities, set_capabilities, personality
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 killed = False
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):
259 logging.warning(_("Process ID %s is still running in chroot. Killing...") % fn)
260 killed = True
261
262 pid = int(fn, 10)
263 os.kill(pid, killsig)
264 os.waitpid(pid, 0)
265 except OSError, e:
266 pass
267
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
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))