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