]> git.ipfire.org Git - people/ms/pakfire.git/blame - python/pakfire/util.py
Try to kill orphans more aggressively when the do not stop within 3 secs.
[people/ms/pakfire.git] / python / pakfire / util.py
CommitLineData
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
22from __future__ import division
23
0a9a2371 24import fcntl
102c5ee7 25import hashlib
aa7f15fe 26import logging
c07a3ca7 27import math
47a4cb89 28import os
4496b160 29import progressbar
47a4cb89 30import random
47a4cb89 31import shutil
aa7f15fe 32import signal
47a4cb89 33import string
0a9a2371 34import struct
47a4cb89 35import sys
0a9a2371 36import termios
47a4cb89
MT
37import time
38
102c5ee7 39from constants import *
c0fd807c 40from i18n import _
47a4cb89 41
3c0e282f 42# Import binary version of version_compare and capability functions
ed4a9455 43from _pakfire import version_compare, get_capabilities, set_capabilities, personality
102c5ee7 44
e9c20259
MT
45def 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
54def 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
72def 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
81class 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
93def 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
117def 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
140def 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
148def 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
167def 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
185def format_time(s):
186 return "%02d:%02d" % (s // 60, s % 60)
187
188def format_speed(s):
189 return "%sB/s" % format_size(s)
190
191def 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
208def 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
248def 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
7814c06d
MT
274 # Calling ourself again to make sure all processes were killed.
275 orphans_kill(root, killsig=killsig)
276
c07a3ca7
MT
277def scriptlet_interpreter(scriptlet):
278 """
279 This function returns the interpreter of a scriptlet.
280 """
281 # XXX handle ELF?
282 interpreter = None
283
284 for line in scriptlet.splitlines():
285 if line.startswith("#!/"):
286 interpreter = line[2:]
287 interpreter = interpreter.split()[0]
288 break
289
290 return interpreter
291
292def calc_parallelism():
293 """
294 Calculate how many processes to run
295 at the same time.
296
297 We take the log10(number of processors) * factor
298 """
299 num = os.sysconf("SC_NPROCESSORS_CONF")
300 if num == 1:
301 return 2
302 else:
303 return int(round(math.log10(num) * 26))